diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index d5a2c42..9a909b6 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -63,6 +63,6 @@ jobs: # 실행할 작업들에 대한 설정 sudo docker pull ${{secrets.DOCKERHUB_USERNAME}}/${{secrets.DOCKERHUB_IMAGE}} sudo docker ps -q | xargs -r sudo docker stop sudo docker ps -aq | xargs -r sudo docker rm - sudo docker run --name redis --rm -d -p 6379:6379 redis - sudo docker run --name ${{secrets.DOCKERHUB_IMAGE}} --rm -d -p 8080:8080 ${{secrets.DOCKERHUB_USERNAME}}/${{secrets.DOCKERHUB_IMAGE}} + sudo docker run --name redis --rm -d -p 6379:6379 --net my-network redis + sudo docker run --name ${{secrets.DOCKERHUB_IMAGE}} --rm -d --net my-network -p 8080:8080 ${{secrets.DOCKERHUB_USERNAME}}/${{secrets.DOCKERHUB_IMAGE}} sudo docker system prune -f \ No newline at end of file diff --git a/build.gradle b/build.gradle index cbbca56..5d2591d 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,9 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'//AWS implementation 'org.springframework.boot:spring-boot-starter-data-redis'//Redis + + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' //localDateTime json변환을 위함. + implementation 'com.fasterxml.jackson.core:jackson-databind' } tasks.named('test') { diff --git a/src/main/java/com/hackathonteam1/refreshrator/RefreshratorApplication.java b/src/main/java/com/hackathonteam1/refreshrator/RefreshratorApplication.java index 3ea5522..6b348d3 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/RefreshratorApplication.java +++ b/src/main/java/com/hackathonteam1/refreshrator/RefreshratorApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableJpaAuditing +@EnableCaching public class RefreshratorApplication { public static void main(String[] args) { diff --git a/src/main/java/com/hackathonteam1/refreshrator/config/RedisConfig.java b/src/main/java/com/hackathonteam1/refreshrator/config/RedisConfig.java index 4aced21..634ad80 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/config/RedisConfig.java +++ b/src/main/java/com/hackathonteam1/refreshrator/config/RedisConfig.java @@ -1,17 +1,21 @@ package com.hackathonteam1.refreshrator.config; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisKeyValueAdapter; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; +import java.time.Duration; + @Configuration public class RedisConfig { @@ -25,8 +29,27 @@ public RedisConfig(@Value("${spring.data.redis.host}") String redisHost, } @Bean + public CacheManager redisCacheManager(){ + RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig() //Redis 캐시 설정을 정의하기 위한 클래스, 기본 캐시 구성 가져옴. + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) //key를 String으로 직렬화 + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) //value를 json으로 직렬화 + .entryTtl(Duration.ofMinutes(15L)); //캐시에 15분 저장 + + return RedisCacheManager.RedisCacheManagerBuilder + .fromConnectionFactory(redisConnectionFactoryForDb1()) //redis db1로 연결 + .cacheDefaults(configuration) + .build(); + } + + @Bean + @Primary public RedisConnectionFactory redisConnectionFactory(){ - return new LettuceConnectionFactory(redisHost, redisPort); + return createLettuceConnectionFactory(0); + } + + @Bean + public RedisConnectionFactory redisConnectionFactoryForDb1(){ + return createLettuceConnectionFactory(1); } @Bean @@ -39,4 +62,10 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisConn redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); //Hash의 value를 json으로 시리얼라이즈 return redisTemplate; } -} + + private LettuceConnectionFactory createLettuceConnectionFactory(int database){ + LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisHost, redisPort); + lettuceConnectionFactory.setDatabase(database); + return lettuceConnectionFactory; + } +} \ No newline at end of file diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/request/fridge/AddFridgeDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/request/fridge/AddFridgeDto.java index 6a0370e..8c13b9e 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/request/fridge/AddFridgeDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/request/fridge/AddFridgeDto.java @@ -3,12 +3,14 @@ import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import java.time.LocalDate; import java.util.UUID; @Getter @AllArgsConstructor +@NoArgsConstructor public class AddFridgeDto { //재료 diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/request/recipe/ModifyRecipeDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/request/recipe/ModifyRecipeDto.java index a88b5d2..a44221a 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/request/recipe/ModifyRecipeDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/request/recipe/ModifyRecipeDto.java @@ -4,6 +4,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import java.util.UUID; diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/PaginationDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/PaginationDto.java index 87f0df1..f7b0cdb 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/PaginationDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/PaginationDto.java @@ -2,10 +2,12 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import org.springframework.data.domain.Page; @Getter @AllArgsConstructor +@NoArgsConstructor public class PaginationDto { private int currentPage; private int totalPage; diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/file/ImageDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/file/ImageDto.java index 51c0435..004d0e3 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/file/ImageDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/file/ImageDto.java @@ -1,15 +1,15 @@ package com.hackathonteam1.refreshrator.dto.response.file; import com.hackathonteam1.refreshrator.entity.Image; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.*; import java.util.UUID; @Getter @Builder @AllArgsConstructor +@Setter +@NoArgsConstructor public class ImageDto { private UUID id; diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/fridge/FridgeItemDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/fridge/FridgeItemDto.java index 1311655..b277013 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/fridge/FridgeItemDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/fridge/FridgeItemDto.java @@ -1,16 +1,27 @@ package com.hackathonteam1.refreshrator.dto.response.fridge; -import lombok.Builder; -import lombok.Getter; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; + +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import lombok.*; import java.time.LocalDate; import java.util.UUID; @Builder @Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor public class FridgeItemDto { private UUID id; private String ingredientName; private UUID ingredientId; + @JsonSerialize(using = LocalDateSerializer.class) + @JsonDeserialize(using = LocalDateDeserializer.class) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private LocalDate expirationDate; } diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/fridge/FridgeItemListDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/fridge/FridgeItemListDto.java index 409b151..5db3720 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/fridge/FridgeItemListDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/fridge/FridgeItemListDto.java @@ -1,14 +1,14 @@ package com.hackathonteam1.refreshrator.dto.response.fridge; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.*; import java.util.List; @Getter @Builder @AllArgsConstructor +@Setter +@NoArgsConstructor public class FridgeItemListDto { private List coldStorage; // 냉장 private List frozen; // 냉동 diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/ingredient/IngredientDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/ingredient/IngredientDto.java index f4c7a43..5df4038 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/ingredient/IngredientDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/ingredient/IngredientDto.java @@ -1,13 +1,15 @@ package com.hackathonteam1.refreshrator.dto.response.ingredient; import com.hackathonteam1.refreshrator.entity.Ingredient; -import lombok.Builder; -import lombok.Getter; +import lombok.*; import java.util.UUID; @Builder @Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor public class IngredientDto { private UUID id; diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/ingredient/IngredientListDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/ingredient/IngredientListDto.java index 6528848..d3551ce 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/ingredient/IngredientListDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/ingredient/IngredientListDto.java @@ -1,14 +1,14 @@ package com.hackathonteam1.refreshrator.dto.response.ingredient; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.*; import java.util.List; @Getter @Builder @AllArgsConstructor +@Setter +@NoArgsConstructor public class IngredientListDto { private List ingredients; } diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/DetailRecipeDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/DetailRecipeDto.java index c9119c9..5249deb 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/DetailRecipeDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/DetailRecipeDto.java @@ -1,12 +1,15 @@ package com.hackathonteam1.refreshrator.dto.response.recipe; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.hackathonteam1.refreshrator.dto.response.file.ImageDto; import com.hackathonteam1.refreshrator.entity.Image; import com.hackathonteam1.refreshrator.entity.IngredientRecipe; import com.hackathonteam1.refreshrator.entity.Recipe; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.*; import java.time.LocalDateTime; import java.util.List; @@ -16,20 +19,29 @@ @Getter @Builder @AllArgsConstructor +@Setter +@NoArgsConstructor public class DetailRecipeDto { private UUID id; private String name; + private String writer; private List ingredientRecipes; private String cookingStep; private int likeCount; private ImageDto image; + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime createdAt; + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime updatedAt; public static DetailRecipeDto mapping(Recipe recipe, List ingredientRecipes){ List ingredientDtos = ingredientRecipes.stream().map( i->IngredientRecipeResponseDto.changeToDto(i)).collect(Collectors.toList()); - return new DetailRecipeDto(recipe.getId(), recipe.getName(), ingredientDtos, + return new DetailRecipeDto(recipe.getId(), recipe.getName(), recipe.getUser().getName(), ingredientDtos, recipe.getCookingStep(), recipe.getLikeCount(), ImageDto.mapping(recipe.getImage()), recipe.getCreatedAt(), recipe.getUpdatedAt()); } diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/IngredientRecipeResponseDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/IngredientRecipeResponseDto.java index 0597a1f..c765ab7 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/IngredientRecipeResponseDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/IngredientRecipeResponseDto.java @@ -5,11 +5,15 @@ import com.hackathonteam1.refreshrator.entity.IngredientRecipe; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import java.util.UUID; @Getter @AllArgsConstructor +@Setter +@NoArgsConstructor public class IngredientRecipeResponseDto { private UUID id; diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/RecipeDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/RecipeDto.java index 754f39e..264ed46 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/RecipeDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/RecipeDto.java @@ -1,22 +1,39 @@ package com.hackathonteam1.refreshrator.dto.response.recipe; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.hackathonteam1.refreshrator.dto.response.file.ImageDto; import com.hackathonteam1.refreshrator.entity.Recipe; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import java.time.LocalDateTime; import java.util.UUID; @Getter @AllArgsConstructor +@Setter +@NoArgsConstructor public class RecipeDto { private UUID recipeId; private String name; private int likeCount; private ImageDto image; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime createdAt; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime updatedAt; public static RecipeDto mapping(Recipe recipe){ return new RecipeDto(recipe.getId(), recipe.getName(), recipe.getLikeCount(), diff --git a/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/RecipeListDto.java b/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/RecipeListDto.java index 34b920f..8482167 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/RecipeListDto.java +++ b/src/main/java/com/hackathonteam1/refreshrator/dto/response/recipe/RecipeListDto.java @@ -2,9 +2,7 @@ import com.hackathonteam1.refreshrator.dto.response.PaginationDto; import com.hackathonteam1.refreshrator.entity.Recipe; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.*; import org.springframework.data.domain.Page; import java.util.List; @@ -13,6 +11,8 @@ @Getter @AllArgsConstructor @Builder +@NoArgsConstructor +@Setter public class RecipeListDto { private List recipeList; private PaginationDto pagination; diff --git a/src/main/java/com/hackathonteam1/refreshrator/entity/Recipe.java b/src/main/java/com/hackathonteam1/refreshrator/entity/Recipe.java index 30c7f5c..e998126 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/entity/Recipe.java +++ b/src/main/java/com/hackathonteam1/refreshrator/entity/Recipe.java @@ -20,8 +20,7 @@ public class Recipe extends BaseEntity{ @Column(nullable = false) private String name; - @Column(nullable = false) - @Lob + @Column(nullable = false, length = 5000) private String cookingStep; @ManyToOne(fetch = FetchType.LAZY, optional = false) diff --git a/src/main/java/com/hackathonteam1/refreshrator/repository/ImageRepository.java b/src/main/java/com/hackathonteam1/refreshrator/repository/ImageRepository.java index 6fbef8e..01b109a 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/repository/ImageRepository.java +++ b/src/main/java/com/hackathonteam1/refreshrator/repository/ImageRepository.java @@ -1,9 +1,12 @@ package com.hackathonteam1.refreshrator.repository; import com.hackathonteam1.refreshrator.entity.Image; +import com.hackathonteam1.refreshrator.entity.Recipe; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; import java.util.UUID; public interface ImageRepository extends JpaRepository { + Optional findByRecipe(Recipe recipe); } diff --git a/src/main/java/com/hackathonteam1/refreshrator/service/FridgeService.java b/src/main/java/com/hackathonteam1/refreshrator/service/FridgeService.java index 4492264..9feb164 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/service/FridgeService.java +++ b/src/main/java/com/hackathonteam1/refreshrator/service/FridgeService.java @@ -15,6 +15,8 @@ import com.hackathonteam1.refreshrator.repository.FridgeRepository; import com.hackathonteam1.refreshrator.repository.IngredientRepository; import lombok.AllArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.time.LocalDate; @@ -30,6 +32,7 @@ public class FridgeService { private FridgeRepository fridgeRepository; //냉장고에 재료 추가 + @CacheEvict(value = "userIngredientsCache", key = "#user.getId()", cacheManager = "redisCacheManager") public void addIngredientInFridge(AddFridgeDto addFridgeDto, User user){ //재료 찾기 @@ -53,6 +56,7 @@ public void addIngredientInFridge(AddFridgeDto addFridgeDto, User user){ } //냉장고에 재료 정보 수정 메서드 + @CacheEvict(value = "userIngredientsCache", key = "#user.getId()", cacheManager = "redisCacheManager") public void updateIngredientInFridge(UUID fridgeItemId, AddFridgeDto addFridgeDto, User user){ //수정할 재료 찾기 @@ -72,6 +76,7 @@ public void updateIngredientInFridge(UUID fridgeItemId, AddFridgeDto addFridgeDt } //냉장고에 재료 삭제 메서드 + @CacheEvict(value = "userIngredientsCache", key = "#user.getId()", cacheManager = "redisCacheManager") public void deleteIngredientInFridge(UUID fridgeItemId, User user){ //삭제할 재료 찾기 FridgeItem fridgeItem=findFridgeItem(fridgeItemId); @@ -84,6 +89,7 @@ public void deleteIngredientInFridge(UUID fridgeItemId, User user){ } // 냉장고에 모든 재료 조회 + @Cacheable(value = "userIngredientsCache",key = "#user.getId()", cacheManager = "redisCacheManager") public FridgeItemListDto getIngredientsInFridge(User user) { // 유저의 냉장고 찾기 Fridge fridge = findFridge(user); diff --git a/src/main/java/com/hackathonteam1/refreshrator/service/RecipeServiceImpl.java b/src/main/java/com/hackathonteam1/refreshrator/service/RecipeServiceImpl.java index 9299a9d..49572d8 100644 --- a/src/main/java/com/hackathonteam1/refreshrator/service/RecipeServiceImpl.java +++ b/src/main/java/com/hackathonteam1/refreshrator/service/RecipeServiceImpl.java @@ -29,6 +29,10 @@ import com.hackathonteam1.refreshrator.util.S3Uploader; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; import org.springframework.http.MediaType; import org.springframework.data.domain.Page; @@ -42,10 +46,7 @@ import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; @Service @@ -60,7 +61,10 @@ public class RecipeServiceImpl implements RecipeService{ private final ImageRepository imageRepository; private final RecipeLikeRepository recipeLikeRepository; + private static final List IMAGE_EXTENSION = Arrays.asList(".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff", ".svg", ".heic"); + @Override + @Cacheable(value = "recipeListCache",key = "#keyword + '-' + #type + '-' + #page + '-' + #size", cacheManager = "redisCacheManager") public RecipeListDto getList(String keyword, String type, int page, int size) { Sort sort; @@ -85,6 +89,7 @@ public RecipeListDto getList(String keyword, String type, int page, int size) { @Override @Transactional + @CacheEvict(value = "recipeListCache", allEntries = true, cacheManager = "redisCacheManager") public void register(RegisterRecipeDto registerRecipeDto, User user) { Image image = null; @@ -92,6 +97,12 @@ public void register(RegisterRecipeDto registerRecipeDto, User user) { image = findImageByImageId(registerRecipeDto.getImageId()); } + //동일한 재료를 요청할 경우 예외처리 + Set ingredientIdSet = new HashSet<>(registerRecipeDto.getIngredientIds()); + if(ingredientIdSet.size() != registerRecipeDto.getIngredientIds().size()){ + throw new BadRequestException(ErrorCode.DUPLICATED_RECIPE_INGREDIENT); + } + Recipe recipe = Recipe.builder() .name(registerRecipeDto.getName()) .cookingStep(registerRecipeDto.getCookingStep()) @@ -101,11 +112,12 @@ public void register(RegisterRecipeDto registerRecipeDto, User user) { recipeRepository.save(recipe); - registerRecipeDto.getIngredientIds().stream().forEach(i -> registerRecipeIngredient(findIngredientByIngredientId(i),recipe)); + ingredientIdSet.stream().forEach(i -> registerRecipeIngredient(findIngredientByIngredientId(i),recipe)); } //상세조회 @Override + @Cacheable(value = "recipeDetailCache", key = "#recipeId", cacheManager = "redisCacheManager") public DetailRecipeDto getDetail(UUID recipeId) { Recipe recipe = findRecipeByRecipeId(recipeId); List ingredientRecipes = findAllIngredientRecipeByRecipe(recipe); @@ -116,6 +128,12 @@ public DetailRecipeDto getDetail(UUID recipeId) { //레시피명, 조리법 수정 @Override + @Caching( + evict = { + @CacheEvict(value = "recipeListCache", allEntries = true, cacheManager = "redisCacheManager"), + @CacheEvict(value = "recipeDetailCache", key = "#recipeId", cacheManager = "redisCacheManager") + } + ) public void modifyContent(ModifyRecipeDto modifyRecipeDto, User user, UUID recipeId) { Recipe recipe = findRecipeByRecipeId(recipeId); checkAuth(recipe.getUser(), user); @@ -137,15 +155,26 @@ public void modifyContent(ModifyRecipeDto modifyRecipeDto, User user, UUID recip //레시피 삭제 @Override + @Caching( + evict = { + @CacheEvict(value = "recipeListCache", allEntries = true, cacheManager = "redisCacheManager"), + @CacheEvict(value = "recipeDetailCache", key = "#recipeId", cacheManager = "redisCacheManager") + } + ) public void delete(UUID recipeId, User user) { Recipe recipe = findRecipeByRecipeId(recipeId); checkAuth(recipe.getUser(), user); + if(recipe.isContainingImage()){ + Image image = findImageByRecipe(recipe); + s3Uploader.removeS3File(image.getUrl().split("/")[3]); + } recipeRepository.delete(recipe); } //레시피 재료 등록 @Override @Transactional + @CacheEvict(value = "recipeDetailCache", key = "#recipeId", cacheManager = "redisCacheManager") public void registerIngredientRecipe(User user, UUID recipeId, RegisterIngredientRecipesDto registerIngredientRecipesDto) { Recipe recipe = findRecipeByRecipeId(recipeId); checkAuth(recipe.getUser(), user); @@ -168,6 +197,7 @@ public void registerIngredientRecipe(User user, UUID recipeId, RegisterIngredien } @Override + @CacheEvict(value = "recipeDetailCache", key = "#recipeId", cacheManager = "redisCacheManager") public void deleteIngredientRecipe(User user, UUID recipeId, DeleteIngredientRecipesDto deleteIngredientRecipesDto) { Recipe recipe = findRecipeByRecipeId(recipeId); checkAuth(recipe.getUser(),user); @@ -212,11 +242,10 @@ public RecipeListDto getRecommendation(int page, int size, int match, String typ @Override public ImageDto registerImage(MultipartFile file) { - if(!file.getContentType().equals(MediaType.IMAGE_GIF_VALUE) && - !file.getContentType().equals(MediaType.IMAGE_PNG_VALUE) && - !file.getContentType().equals(MediaType.IMAGE_JPEG_VALUE) ){ + String fileName = file.getOriginalFilename(); + if(!IMAGE_EXTENSION.stream().anyMatch(i-> fileName.endsWith(i))){ throw new FileStorageException(ErrorCode.FILE_TYPE_ERROR); - } + }; String url; try { @@ -235,9 +264,15 @@ public ImageDto registerImage(MultipartFile file) { } @Override + @Caching( + evict = { + @CacheEvict(value = "recipeListCache", allEntries = true, cacheManager = "redisCacheManager"), + } + ) public void deleteImage(UUID imageId, User user) { Image image = findImageByImageId(imageId); Recipe recipe = image.getRecipe(); + UUID recipeId = recipe.getId(); if(!recipe.isContainingImage()){ throw new BadRequestException(ErrorCode.IMAGE_NOT_IN_RECIPE); @@ -291,6 +326,12 @@ private void registerRecipeIngredient(Ingredient ingredient, Recipe recipe){ // 레시피에 좋아요 추가 @Override + @Caching( + evict = { + @CacheEvict(value = "recipeListCache", allEntries = true, cacheManager = "redisCacheManager"), + @CacheEvict(value = "recipeDetailCache", key = "#recipeId", cacheManager = "redisCacheManager") + } + ) public void addLikeToRecipe(User user, UUID recipeId){ Recipe recipe = findRecipeByRecipeId(recipeId); @@ -305,6 +346,12 @@ public void addLikeToRecipe(User user, UUID recipeId){ // 레시피에 좋아요 삭제 @Override + @Caching( + evict = { + @CacheEvict(value = "recipeListCache", allEntries = true, cacheManager = "redisCacheManager"), + @CacheEvict(value = "recipeDetailCache", key = "#recipeId", cacheManager = "redisCacheManager") + } + ) public void deleteLikeFromRecipe(User user, UUID recipeId){ Recipe recipe = findRecipeByRecipeId(recipeId); // 해당 레시피에서 내가 누른 좋아요 반환 @@ -358,4 +405,8 @@ private void checkValidPage(Page pages, int page){ throw new NotFoundException(ErrorCode.PAGE_NOT_FOUND); } } + + private Image findImageByRecipe(Recipe recipe){ + return imageRepository.findByRecipe(recipe).orElseThrow(()->new NotFoundException(ErrorCode.IMAGE_NOT_FOUND)); + } }