diff --git a/src/main/java/com/server/capple/config/RedisConfig.java b/src/main/java/com/server/capple/config/RedisConfig.java index 1302fd44..12ab9adf 100644 --- a/src/main/java/com/server/capple/config/RedisConfig.java +++ b/src/main/java/com/server/capple/config/RedisConfig.java @@ -53,6 +53,15 @@ public CacheManager noExpireCacheManager(RedisConnectionFactory redisConnectionF return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory).cacheDefaults(redisCacheConfiguration).build(); } + @Bean + public CacheManager oneDayExpireCacheManager(RedisConnectionFactory redisConnectionFactory) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) + .entryTtl(Duration.ofDays(1)); + return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory).cacheDefaults(redisCacheConfiguration).build(); + } + @Bean public CacheManager apnsJwtCacheManager(RedisConnectionFactory redisCloudConnectionFactory) { RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() diff --git a/src/main/java/com/server/capple/domain/answer/repository/AnswerRepository.java b/src/main/java/com/server/capple/domain/answer/repository/AnswerRepository.java index 06e69b6a..72393a0e 100644 --- a/src/main/java/com/server/capple/domain/answer/repository/AnswerRepository.java +++ b/src/main/java/com/server/capple/domain/answer/repository/AnswerRepository.java @@ -28,4 +28,7 @@ public interface AnswerRepository extends JpaRepository { Slice findByQuestion(@Param("questionId") Long questionId, Long lastIndex, Pageable pageable); Slice findByMemberAndIdIsLessThanEqual(@Param("member") Member member, Long lastIndex, Pageable pageable); + + @Query("SELECT COUNT(a) FROM Answer a WHERE a.question.id = :questionId") + Integer getAnswerCountByQuestionId(Long questionId); } diff --git a/src/main/java/com/server/capple/domain/answer/service/AnswerCountService.java b/src/main/java/com/server/capple/domain/answer/service/AnswerCountService.java new file mode 100644 index 00000000..05f6c8a6 --- /dev/null +++ b/src/main/java/com/server/capple/domain/answer/service/AnswerCountService.java @@ -0,0 +1,27 @@ +package com.server.capple.domain.answer.service; + +import com.server.capple.domain.answer.repository.AnswerRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.concurrent.CompletableFuture; + +@Service +@RequiredArgsConstructor +public class AnswerCountService { + private final AnswerRepository answerRepository; + + @Cacheable(value = "questionAnswer", key = "#questionId", cacheManager = "oneDayExpireCacheManager") + public Integer getQuestionAnswerCount(Long questionId) { + return answerRepository.getAnswerCountByQuestionId(questionId); + } + + @Async + @CachePut(value = "questionAnswer", key = "#questionId", cacheManager = "oneDayExpireCacheManager") + public CompletableFuture updateQuestionAnswerCount(Long questionId) { + return CompletableFuture.completedFuture(answerRepository.getAnswerCountByQuestionId(questionId)); + } +} diff --git a/src/main/java/com/server/capple/domain/answer/service/AnswerServiceImpl.java b/src/main/java/com/server/capple/domain/answer/service/AnswerServiceImpl.java index e1ee29d2..6607e6cf 100644 --- a/src/main/java/com/server/capple/domain/answer/service/AnswerServiceImpl.java +++ b/src/main/java/com/server/capple/domain/answer/service/AnswerServiceImpl.java @@ -18,11 +18,16 @@ import com.server.capple.global.common.SliceResponse; import com.server.capple.global.exception.RestApiException; import com.server.capple.global.exception.errorCode.AnswerErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; @Service @RequiredArgsConstructor @@ -34,6 +39,8 @@ public class AnswerServiceImpl implements AnswerService { private final AnswerMapper answerMapper; private final MemberService memberService; private final AnswerHeartRedisRepository answerHeartRedisRepository; + private final AnswerCountService answerCountService; + private final ApplicationEventPublisher applicationEventPublisher; @Transactional @Override @@ -49,7 +56,7 @@ public AnswerResponse.AnswerId createAnswer(Member loginMember, Long questionId, //답변 저장 Answer answer = answerRepository.save(answerMapper.toAnswerEntity(request, member, question)); // answer.getQuestion().increaseCommentCount(); - + applicationEventPublisher.publishEvent(new QuestionAnswerCountChangedEvent(questionId)); return new AnswerResponse.AnswerId(answer.getId()); } @@ -71,13 +78,12 @@ public AnswerResponse.AnswerId updateAnswer(Member loginMember, Long answerId, A @Transactional public AnswerResponse.AnswerId deleteAnswer(Member loginMember, Long answerId) { Answer answer = findAnswer(answerId); + applicationEventPublisher.publishEvent(new QuestionAnswerCountChangedEvent(answer.getQuestion().getId())); checkPermission(loginMember, answer); // answer.getQuestion().decreaseCommentCount(); - answer.delete(); - return new AnswerResponse.AnswerId(answerId); } @@ -104,7 +110,7 @@ public SliceResponse getAnswerList(Long memberId, Long questionId, L answerHeartRedisRepository.isMemberLikedAnswer(memberId, answerInfoDto.getAnswer().getId()), answerInfoDto.getAnswer().getMember().getId().equals(memberId) ) - ).toList(), lastIndex.toString(), null); + ).toList(), lastIndex.toString(), answerCountService.getQuestionAnswerCount(questionId)); } // 유저가 작성한 답변 조회 @@ -164,4 +170,15 @@ private Long getLastIndexFromAnswerInfoInterface(Long lastIndex, Slice answerSlice) { return (!answerSlice.getContent().isEmpty() && lastIndex == Long.MAX_VALUE) ? answerSlice.stream().map(Answer::getId).max(Long::compareTo).get() : lastIndex; } + + @Getter + @AllArgsConstructor + static class QuestionAnswerCountChangedEvent { + private Long questionId; + } + + @TransactionalEventListener(classes = QuestionAnswerCountChangedEvent.class, phase = TransactionPhase.AFTER_COMPLETION) + public void handleQuestionCreatedEvent(QuestionAnswerCountChangedEvent event) { + answerCountService.updateQuestionAnswerCount(event.getQuestionId()); + } }