diff --git a/build.gradle b/build.gradle index 4ad16b9..cbabfc2 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' + // redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' // jwt implementation 'io.jsonwebtoken:jjwt-api:0.11.5' diff --git a/src/main/java/com/damyo/alpha/AlphaApplication.java b/src/main/java/com/damyo/alpha/AlphaApplication.java index 4014f0a..883f8a2 100644 --- a/src/main/java/com/damyo/alpha/AlphaApplication.java +++ b/src/main/java/com/damyo/alpha/AlphaApplication.java @@ -3,9 +3,11 @@ import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; 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; import org.springframework.scheduling.annotation.EnableScheduling; +@EnableCaching @EnableJpaAuditing @EnableScheduling @EnableBatchProcessing diff --git a/src/main/java/com/damyo/alpha/api/info/service/InfoService.java b/src/main/java/com/damyo/alpha/api/info/service/InfoService.java index fd7c21c..0856f55 100644 --- a/src/main/java/com/damyo/alpha/api/info/service/InfoService.java +++ b/src/main/java/com/damyo/alpha/api/info/service/InfoService.java @@ -13,6 +13,9 @@ import com.damyo.alpha.api.user.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.List; @@ -30,6 +33,7 @@ public class InfoService { private static final int POST_INFO_CONTRIBUTION_INCREMENT = 5; + @CacheEvict(value={"areaDetailsCache", "areaSummaryCache"}, key="#updateInfoRequest.smokingAreaId()", cacheManager="contentCacheManager") public void updateInfo(UpdateInfoRequest updateInfoRequest, UserDetailsImpl details) { SmokingArea sa = smokingAreaRepository.findSmokingAreaById(updateInfoRequest.smokingAreaId()) .orElseThrow(() -> { @@ -38,6 +42,10 @@ public void updateInfo(UpdateInfoRequest updateInfoRequest, UserDetailsImpl deta }); User user = details.getUser(); infoRepository.save(updateInfoRequest.toEntity(sa, user)); + int size = infoRepository.findInfosBySmokingAreaId(updateInfoRequest.smokingAreaId()).size(); + log.info("[test]: {}", size); + Float score = (sa.getScore() * size + updateInfoRequest.score()) / (size + 1); + smokingAreaRepository.updateSmokingAreaScoreById(score, sa.getId()); userService.updateContribution(user.getId(), POST_INFO_CONTRIBUTION_INCREMENT); log.info("[Info]: info update complete"); } diff --git a/src/main/java/com/damyo/alpha/api/picture/controller/dto/PictureResponse.java b/src/main/java/com/damyo/alpha/api/picture/controller/dto/PictureResponse.java index 12ecd14..ded28f0 100644 --- a/src/main/java/com/damyo/alpha/api/picture/controller/dto/PictureResponse.java +++ b/src/main/java/com/damyo/alpha/api/picture/controller/dto/PictureResponse.java @@ -1,11 +1,17 @@ package com.damyo.alpha.api.picture.controller.dto; import com.damyo.alpha.api.picture.domain.Picture; +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 java.time.LocalDateTime; public record PictureResponse ( Long id, String pictureUrl, + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) LocalDateTime createdAt, Long likes ) { diff --git a/src/main/java/com/damyo/alpha/api/picture/service/PictureService.java b/src/main/java/com/damyo/alpha/api/picture/service/PictureService.java index 20c653a..dcdfa55 100644 --- a/src/main/java/com/damyo/alpha/api/picture/service/PictureService.java +++ b/src/main/java/com/damyo/alpha/api/picture/service/PictureService.java @@ -14,6 +14,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.redis.core.HashOperations; @@ -43,6 +44,7 @@ public class PictureService { @Autowired private RedisTemplate countTemplate; + @Cacheable(value="pictureCache", key="#id", cacheManager="contentCacheManager") public PictureResponse getPicture(Long id) { Picture picture = pictureRepository.findPictureById(id) .orElseThrow(() -> { @@ -53,12 +55,14 @@ public PictureResponse getPicture(Long id) { return new PictureResponse(picture); } + @Cacheable(value="pictureUserCache", key="#id", cacheManager="contentCacheManager") public List getPicturesByUser(UUID id) { List pictures = pictureRepository.findPicturesByUser_id(id); log.info("[Picture]: load pictures by user | {}", id); return getPictureListResponse(pictures); } + @Cacheable(value="pictureAreaCache", key="#id", cacheManager="contentCacheManager") public List getPicturesBySmokingArea(String id, Long count) { List pictures = pictureRepository.findPicturesBySmokingArea_Id(id, count); log.info("[Picture]: load pictures by area | {}", id); diff --git a/src/main/java/com/damyo/alpha/api/smokingarea/controller/SmokingAreaController.java b/src/main/java/com/damyo/alpha/api/smokingarea/controller/SmokingAreaController.java index fdec4a4..bf4a140 100644 --- a/src/main/java/com/damyo/alpha/api/smokingarea/controller/SmokingAreaController.java +++ b/src/main/java/com/damyo/alpha/api/smokingarea/controller/SmokingAreaController.java @@ -46,7 +46,6 @@ public class SmokingAreaController { private final SmokingAreaService smokingAreaService; private final PictureService pictureService; private final UserService userService; - private final InfoService infoService; private final S3ImageService s3ImageService; private final RedisTemplate redisTemplate; @@ -108,13 +107,12 @@ public ResponseEntity getSmokingAreaDetailsById( @Parameter(description = "흡연구역 ID", in = ParameterIn.PATH) @PathVariable String smokingAreaId){ log.info("[Area]: /details/{}", smokingAreaId); - SmokingAreaDetailResponse area = smokingAreaService.findAreaById(smokingAreaId).toDTO(); - InfoResponse info = infoService.getInfo(smokingAreaId); - Float avgScore = Math.round((info.score() + area.score()) / (info.size()+1) * 10) / 10.0F; + + SmokingAreaDetailResponse area = smokingAreaService.findAreaDTOById(smokingAreaId); List picList = pictureService.getPicturesBySmokingArea(smokingAreaId, 10L); SmokingAreaAllResponse response = new SmokingAreaAllResponse(area.areaId(), area.name(), area.latitude(), area.longitude(), area.address(), - area.createdAt(), area.description(), avgScore, area.status(), area.opened(), area.closed(), + area.createdAt(), area.description(), area.score(), area.status(), area.opened(), area.closed(), area.indoor(), area.outdoor(), picList); return ResponseEntity.ok(response); @@ -129,9 +127,9 @@ public ResponseEntity getSmokingAreaSummaryById( @Parameter(description = "흡연구역 ID", in = ParameterIn.PATH) @PathVariable String smokingAreaId){ log.info("[Area]: /summary/{}", smokingAreaId); - SmokingArea areaResponse = smokingAreaService.findAreaById(smokingAreaId); + SmokingAreaSummaryResponse response = smokingAreaService.findAreaSUMById(smokingAreaId); - return ResponseEntity.ok(areaResponse.toSUM()); + return ResponseEntity.ok(response); } // 특정날짜이후 추가된 구역찾기 diff --git a/src/main/java/com/damyo/alpha/api/smokingarea/controller/dto/SmokingAreaDetailResponse.java b/src/main/java/com/damyo/alpha/api/smokingarea/controller/dto/SmokingAreaDetailResponse.java index bc3d88d..af048ac 100644 --- a/src/main/java/com/damyo/alpha/api/smokingarea/controller/dto/SmokingAreaDetailResponse.java +++ b/src/main/java/com/damyo/alpha/api/smokingarea/controller/dto/SmokingAreaDetailResponse.java @@ -1,5 +1,10 @@ package com.damyo.alpha.api.smokingarea.controller.dto; +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 java.math.BigDecimal; import java.time.LocalDateTime; @@ -9,6 +14,8 @@ public record SmokingAreaDetailResponse( BigDecimal latitude, BigDecimal longitude, String address, + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) LocalDateTime createdAt, Boolean status, String description, diff --git a/src/main/java/com/damyo/alpha/api/smokingarea/domain/SmokingAreaRepository.java b/src/main/java/com/damyo/alpha/api/smokingarea/domain/SmokingAreaRepository.java index 2377b90..ed20372 100644 --- a/src/main/java/com/damyo/alpha/api/smokingarea/domain/SmokingAreaRepository.java +++ b/src/main/java/com/damyo/alpha/api/smokingarea/domain/SmokingAreaRepository.java @@ -53,6 +53,12 @@ public interface SmokingAreaRepository extends JpaRepository findAreaAll(){ return areaResponses; } - public SmokingArea findAreaById(String smokingAreaId) { + @Cacheable(value="areaDetailsCache", key="#smokingAreaId", cacheManager="contentCacheManager") + public SmokingAreaDetailResponse findAreaDTOById(String smokingAreaId) { SmokingArea area = smokingAreaRepository.findSmokingAreaById(smokingAreaId) .orElseThrow(() -> { - log.error("[Area]: area not found by id | {}", smokingAreaId); + log.error("[Area]: area detail not found by id | {}", smokingAreaId); return new AreaException(NOT_FOUND_ID); }); - return area; + return area.toDTO(); + } + + @Cacheable(value="areaSummaryCache", key="#smokingAreaId", cacheManager="contentCacheManager") + public SmokingAreaSummaryResponse findAreaSUMById(String smokingAreaId) { + SmokingArea area = smokingAreaRepository.findSmokingAreaById(smokingAreaId) + .orElseThrow(() -> { + log.error("[Area]: area summary not found by id | {}", smokingAreaId); + return new AreaException(NOT_FOUND_ID); + }); + return area.toSUM(); } public List findAreaByCreatedAt(LocalDateTime createdAt) { diff --git a/src/main/java/com/damyo/alpha/api/smokingdata/service/SmokingDataService.java b/src/main/java/com/damyo/alpha/api/smokingdata/service/SmokingDataService.java index affdbec..0cc065d 100644 --- a/src/main/java/com/damyo/alpha/api/smokingdata/service/SmokingDataService.java +++ b/src/main/java/com/damyo/alpha/api/smokingdata/service/SmokingDataService.java @@ -9,6 +9,7 @@ import com.damyo.alpha.api.user.domain.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.time.LocalDateTime; diff --git a/src/main/java/com/damyo/alpha/api/star/service/StarService.java b/src/main/java/com/damyo/alpha/api/star/service/StarService.java index b5d4424..dc189e1 100644 --- a/src/main/java/com/damyo/alpha/api/star/service/StarService.java +++ b/src/main/java/com/damyo/alpha/api/star/service/StarService.java @@ -13,6 +13,7 @@ import com.damyo.alpha.api.user.domain.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.ArrayList; diff --git a/src/main/java/com/damyo/alpha/global/config/RedisCacheConfig.java b/src/main/java/com/damyo/alpha/global/config/RedisCacheConfig.java new file mode 100644 index 0000000..0854d4b --- /dev/null +++ b/src/main/java/com/damyo/alpha/global/config/RedisCacheConfig.java @@ -0,0 +1,28 @@ +package com.damyo.alpha.global.config; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; + +@Configuration +@EnableCaching +public class RedisCacheConfig { + @Bean + public CacheManager contentCacheManager(RedisConnectionFactory rcf) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) + .entryTtl(Duration.ofMinutes(30L)); + + return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(rcf).cacheDefaults(redisCacheConfiguration).build(); + } +}