From 5b2ceb42f45748d010f2dac799c8cdfe15da3ae2 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 26 May 2024 16:12:47 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[#92]=20certifyMemeMember=EC=9D=98=20static?= =?UTF-8?q?=EC=9D=84=20=EC=A0=9C=EA=B1=B0=ED=95=98=EA=B3=A0=20=EA=B7=B8?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20Test=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/domain/meme/service/MemeService.java | 2 +- .../domain/meme/service/MemeServiceTest.java | 12 ++++++++++++ .../domain/memes/service/MemesServiceTest.java | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/meme/service/MemeService.java b/backend/memetory/src/main/java/com/example/memetory/domain/meme/service/MemeService.java index 227b81b1..e449aa6c 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/meme/service/MemeService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/meme/service/MemeService.java @@ -60,7 +60,7 @@ public MemeResponse findMemberMemeResponse(MemeServiceDto memeServiceDto) { return MemeResponse.of(meme); } - public static void certifyMemeMember(Member m1, Member m2) { + public void certifyMemeMember(Member m1, Member m2) { if (!m1.equals(m2)) { throw new AccessDeniedMemeException(); } diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/meme/service/MemeServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/meme/service/MemeServiceTest.java index 057ebfff..dc65a254 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/meme/service/MemeServiceTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/meme/service/MemeServiceTest.java @@ -28,6 +28,7 @@ import com.example.memetory.domain.meme.dto.MemeResponse; import com.example.memetory.domain.meme.dto.MemeServiceDto; import com.example.memetory.domain.meme.entity.Meme; +import com.example.memetory.domain.meme.exception.AccessDeniedMemeException; import com.example.memetory.domain.meme.exception.NotFoundMemeException; import com.example.memetory.domain.meme.repository.MemeRepository; @@ -135,4 +136,15 @@ private List createMemeResponseList(int pageSize) { return memeResponseList; } + + @Test + @DisplayName("밈 멤버와 다른 멤버로 인한 AccessDeniedMemeException 반환") + void Given_differentMember_When_certifyMemeMember_Throw_AccessDeniedMemeException() { + // given + Member differentMember = OTHER_MEMBER(); + + // then + assertThrows(AccessDeniedMemeException.class, () -> memeService.certifyMemeMember(member, differentMember)); + } + } diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/MemesServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/MemesServiceTest.java index acc5856a..e6623f5e 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/MemesServiceTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/MemesServiceTest.java @@ -80,6 +80,9 @@ void Given_unAuthorizedMember_When_registerMemes_Throw_NotAccessMemeException() given(memeService.findMemeFromId(memesServiceDto.getMemeId())).willReturn(meme); given(memberService.findMemberFromEmail(any(String.class))).willReturn(unAuthorizedMember); + doThrow(new AccessDeniedMemeException()).when(memeService) + .certifyMemeMember(any(Member.class), any(Member.class)); + // then assertThrows(AccessDeniedMemeException.class, () -> memesService.registerMemes(memesServiceDto)); } From 8e6125f4563c8a06eb337b50d625ed54f70f795f Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 26 May 2024 20:02:21 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[#92]=20RedisConfig=EC=97=90=20RedisTamplat?= =?UTF-8?q?e=20=EB=B9=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/global/config/RedisConfig.java | 27 ++++++++++++- .../memetory/global/util/PasswordUtil.java | 38 +++++++++---------- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java b/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java index 09aef7f6..2dc32a44 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java @@ -1,12 +1,21 @@ package com.example.memetory.global.config; +import java.time.LocalDate; + +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration +@EnableRedisRepositories public class RedisConfig { @Value("${spring.redis.host}") private String host; @@ -16,6 +25,22 @@ public class RedisConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { - return new LettuceConnectionFactory(host, port); + return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port)); + } + + @Bean(name = "rankingRedisTemplate") + public RedisTemplate rankingRedisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + return redisTemplate; + } + + @Bean(name = "rankingZSetOperations") + public ZSetOperations rankingZSetOperations( + @Qualifier ("rankingRedisTemplate") RedisTemplate redisTemplate) { + return redisTemplate.opsForZSet(); } } diff --git a/backend/memetory/src/main/java/com/example/memetory/global/util/PasswordUtil.java b/backend/memetory/src/main/java/com/example/memetory/global/util/PasswordUtil.java index 46a3b420..eae9daab 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/util/PasswordUtil.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/util/PasswordUtil.java @@ -4,27 +4,25 @@ public class PasswordUtil { - public static String generateRandomPassword() { - int index = 0; - char[] charSet = new char[] { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' - }; //배열안의 문자 숫자는 원하는대로 + public static String generateRandomPassword() { + int index = 0; + char[] charSet = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' + }; - StringBuffer password = new StringBuffer(); - Random random = new Random(); + StringBuffer password = new StringBuffer(); + Random random = new Random(); - for (int i = 0; i < 8 ; i++) { - double rd = random.nextDouble(); - index = (int) (charSet.length * rd); + for (int i = 0; i < 8; i++) { + double rd = random.nextDouble(); + index = (int)(charSet.length * rd); - password.append(charSet[index]); - } - System.out.println(password); - return password.toString(); - //StringBuffer를 String으로 변환해서 return 하려면 toString()을 사용하면 된다. - } + password.append(charSet[index]); + } + return password.toString(); + } } From 4de0a4efc0d7c06a0c067a15ce1ff751c9399547 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 26 May 2024 22:23:40 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[#92]=20RankingService=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=20=EC=88=98=20=EC=A6=9D=EA=B0=80=EC=99=80=20=EA=B0=90?= =?UTF-8?q?=EC=86=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/memes/service/RankingService.java | 51 +++++++++++ .../memetory/global/config/RedisConfig.java | 3 +- .../memes/service/RankingServiceTest.java | 84 +++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java create mode 100644 backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java new file mode 100644 index 00000000..3a5bb49b --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java @@ -0,0 +1,51 @@ +package com.example.memetory.domain.memes.service; + +import java.time.LocalDate; + +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Service; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Service +public class RankingService { + private final String PREFIX = "LIKE_RANKING_DATE::"; + private final ZSetOperations rankingZSet; + + @Transactional + public void increaseTodayMemesLikeCountFromMemesId(Long memesId) { + String key = PREFIX + LocalDate.now(); + + if (isExistedTodayMemesId(key, memesId)) { + rankingZSet.incrementScore(key, memesId, 1); + return; + } + saveTodayMemesLikeCount(key, memesId); + } + + private boolean isExistedTodayMemesId(String key, Long memesId) { + Double score = rankingZSet.score(key, memesId); + + if (score == null) { + return false; + } + return true; + } + + private void saveTodayMemesLikeCount(String key, Long memesId) { + rankingZSet.add(key, memesId, 1); + } + + @Transactional + public void decreaseTodayMemesLikeCountFromMemesId(Long memesId) { + String key = PREFIX + LocalDate.now(); + + rankingZSet.incrementScore(key, memesId, -1); + } + + // 현재 - 일주일 전(Key) 랭킹을 구함 + + // 현재 - 한달 전(Key) 랭킹을 구함 +} diff --git a/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java b/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java index 2dc32a44..0b6e8d2e 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java @@ -12,6 +12,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @@ -32,7 +33,7 @@ public RedisConnectionFactory redisConnectionFactory() { public RedisTemplate rankingRedisTemplate() { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Long.class)); redisTemplate.setConnectionFactory(redisConnectionFactory()); return redisTemplate; diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java new file mode 100644 index 00000000..c726c529 --- /dev/null +++ b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java @@ -0,0 +1,84 @@ +package com.example.memetory.domain.memes.service; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDate; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.ZSetOperations; + +import jakarta.transaction.Transactional; + +@DisplayName("Ranking 서비스 테스트의 ") +@SpringBootTest +@Transactional +public class RankingServiceTest { + private static final String PREFIX = "LIKE_RANKING_DATE::"; + static String key; + + private final Long MEMES_ID = -1L; + + @Autowired + private RankingService rankingService; + @Autowired + private ZSetOperations rankingZSet; + + @BeforeAll + public static void setUp() { + key = PREFIX + LocalDate.now(); + } + + @AfterEach + void clearRedis() { + rankingZSet.remove(key, MEMES_ID); + } + + @Test + @DisplayName("존재하지 않는 memes와 memesId를 통한 score 증가 성송") + void Given_memesId_When_increaseTodayMemesLikeCountFromMemesId_Then_Memems_Score() { + // given + Double expectedScore = 1.0; + + // when + rankingService.increaseTodayMemesLikeCountFromMemesId(MEMES_ID); + + // then + assertThat(rankingZSet.score(key, MEMES_ID)).isEqualTo(expectedScore); + } + + @Test + @DisplayName("존재하는 memes와 memesId를 통한 score 증가 성공") + void Given_existMemesAndMemesId_When_increaseTodayMemesLikeCountFromMemesId_Then_Memes_Score() { + // given + Double expectedScore = 2.0; + + rankingZSet.add(key, MEMES_ID, 1); + + // when + rankingService.increaseTodayMemesLikeCountFromMemesId(MEMES_ID); + + // then + assertThat(rankingZSet.score(key, MEMES_ID)).isEqualTo(expectedScore); + } + + @Test + @DisplayName("존재하는 memes와 memesId를 통한 score 증가") + void Given_existMemesAndMemesId_When_decreaseTodayMemesLikeCountFromMemesId_Then_Memes_Score() { + // given + Double expectedScore = 1.0; + + rankingZSet.add(key, MEMES_ID, 2); + + // when + rankingService.decreaseTodayMemesLikeCountFromMemesId(MEMES_ID); + + // then + assertThat(rankingZSet.score(key, MEMES_ID)).isEqualTo(expectedScore); + } + +} From d87295dc0dee70621d017c8417ff15c8fc6969b0 Mon Sep 17 00:00:00 2001 From: JunRain Date: Mon, 27 May 2024 15:18:05 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[#92]Redis=EC=97=90=20Transactional=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Redis는 롤백을 지원하지 않는다. 따라서 Redis는 명령어를 큐에 넣어둔 다음, 큐에 쌓인 명령어들이 실행되는 도안 다른 명령어가 실행되지않음을 보장한다. --- .../memetory/global/config/RedisConfig.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java b/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java index 0b6e8d2e..31fbb0a8 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/config/RedisConfig.java @@ -1,7 +1,5 @@ package com.example.memetory.global.config; -import java.time.LocalDate; - import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -14,9 +12,13 @@ import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableRedisRepositories +@EnableTransactionManagement public class RedisConfig { @Value("${spring.redis.host}") private String host; @@ -34,6 +36,8 @@ public RedisTemplate rankingRedisTemplate() { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Long.class)); + redisTemplate.setEnableTransactionSupport(true); + redisTemplate.setConnectionFactory(redisConnectionFactory()); return redisTemplate; @@ -41,7 +45,12 @@ public RedisTemplate rankingRedisTemplate() { @Bean(name = "rankingZSetOperations") public ZSetOperations rankingZSetOperations( - @Qualifier ("rankingRedisTemplate") RedisTemplate redisTemplate) { + @Qualifier("rankingRedisTemplate") RedisTemplate redisTemplate) { return redisTemplate.opsForZSet(); } + + @Bean + public PlatformTransactionManager transactionManager() { + return new JpaTransactionManager(); + } } From 4b494ecbf9524f4156ba0b502c1ee4770498ed3f Mon Sep 17 00:00:00 2001 From: JunRain Date: Mon, 27 May 2024 15:22:50 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[#92]=20RankingService=20=EC=A3=BC=EA=B0=84?= =?UTF-8?q?/=EC=9B=94=EA=B0=84=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/memes/service/RankingService.java | 68 +++++++++++++------ .../memes/service/RankingServiceTest.java | 64 ++++++++++++++--- 2 files changed, 101 insertions(+), 31 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java index 3a5bb49b..2974347b 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java @@ -1,51 +1,79 @@ package com.example.memetory.domain.memes.service; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Service public class RankingService { private final String PREFIX = "LIKE_RANKING_DATE::"; + private final String POSTFIX_WEEK = "::WEEK"; + private final String POSTFIX_MONTH = "::MONTH"; + private final Long TOP_TEN = 10L; + private final ZSetOperations rankingZSet; @Transactional public void increaseTodayMemesLikeCountFromMemesId(Long memesId) { String key = PREFIX + LocalDate.now(); - if (isExistedTodayMemesId(key, memesId)) { - rankingZSet.incrementScore(key, memesId, 1); - return; - } - saveTodayMemesLikeCount(key, memesId); + rankingZSet.incrementScore(key, memesId, 1); } - private boolean isExistedTodayMemesId(String key, Long memesId) { - Double score = rankingZSet.score(key, memesId); + // Todo 음수를 확인하는 로직 추가 필요해 보임 + @Transactional + public void decreaseTodayMemesLikeCountFromMemesId(Long memesId) { + String key = PREFIX + LocalDate.now(); - if (score == null) { - return false; - } - return true; + rankingZSet.incrementScore(key, memesId, -1); } - private void saveTodayMemesLikeCount(String key, Long memesId) { - rankingZSet.add(key, memesId, 1); + @Transactional(readOnly = true) + public List findTopTenMemesLikeCountForWeek() { + String key = PREFIX + LocalDate.now() + POSTFIX_WEEK; + + if (isNotExistedKey(key)) + unionMemesFromKeyAndDay(key, 7); + + Set result = rankingZSet.reverseRange(key, 0, TOP_TEN); + return List.copyOf(result); } - @Transactional - public void decreaseTodayMemesLikeCountFromMemesId(Long memesId) { - String key = PREFIX + LocalDate.now(); + private boolean isNotExistedKey(String key) { + Set check = rankingZSet.range(key, 0, 1); - rankingZSet.incrementScore(key, memesId, -1); + return check.isEmpty(); + } + + private void unionMemesFromKeyAndDay(String key, int day) { + List keyList = new ArrayList<>(); + LocalDate today = LocalDate.now(); + + for (int i = 1; i < day; i++) { + LocalDate date = today.minusDays(i); + keyList.add(PREFIX + date); + } + + rankingZSet.unionAndStore(key, keyList, key); } - // 현재 - 일주일 전(Key) 랭킹을 구함 + @Transactional(readOnly = true) + public List findTopTenMemesLikeCountForMonth() { + String key = PREFIX + LocalDate.now() + POSTFIX_MONTH; - // 현재 - 한달 전(Key) 랭킹을 구함 + if (isNotExistedKey(key)) { + unionMemesFromKeyAndDay(key, 30); + } + + Set result = rankingZSet.reverseRange(key, 0, TOP_TEN); + return List.copyOf(result); + } } diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java index c726c529..6e77c49c 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java @@ -3,43 +3,45 @@ import static org.assertj.core.api.Assertions.*; import java.time.LocalDate; +import java.util.List; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.ZSetOperations; -import jakarta.transaction.Transactional; - @DisplayName("Ranking 서비스 테스트의 ") @SpringBootTest -@Transactional public class RankingServiceTest { - private static final String PREFIX = "LIKE_RANKING_DATE::"; - static String key; - + private final String PREFIX = "LIKE_RANKING_DATE::"; + private final String POSTFIX_WEEK = "::WEEK"; private final Long MEMES_ID = -1L; + private String key; + @Autowired private RankingService rankingService; @Autowired private ZSetOperations rankingZSet; + @Autowired + private RedisConnectionFactory redisConnectionFactory; - @BeforeAll - public static void setUp() { + @BeforeEach + public void setUp() { key = PREFIX + LocalDate.now(); } @AfterEach void clearRedis() { - rankingZSet.remove(key, MEMES_ID); + redisConnectionFactory.getConnection().flushAll(); } @Test - @DisplayName("존재하지 않는 memes와 memesId를 통한 score 증가 성송") + @DisplayName("존재하지 않는 memes와 memesId를 통한 score 증가 성공") void Given_memesId_When_increaseTodayMemesLikeCountFromMemesId_Then_Memems_Score() { // given Double expectedScore = 1.0; @@ -81,4 +83,44 @@ void Given_existMemesAndMemesId_When_decreaseTodayMemesLikeCountFromMemesId_Then assertThat(rankingZSet.score(key, MEMES_ID)).isEqualTo(expectedScore); } + @Test + @DisplayName("존재하지 않는 주간 key를 통한 MemesIdList 반환") + void Given_NotExistKey_When_findTopTenMemesLikeCountForWeek_Then_MemesIdList() { + // given + final int DAY = 7; + + for (int i = 0; i < DAY; i++) { + LocalDate date = LocalDate.now().minusDays(i); + String dateKey = PREFIX + date; + rankingZSet.add(dateKey, (long)i, i); + } + + // when + List result = rankingService.findTopTenMemesLikeCountForWeek(); + + //then + assertThat(result.size()).isEqualTo(DAY - 1); + assertThat(result.get(0)).isEqualTo(DAY - 1); + } + + @Test + @DisplayName("존재하는 주간 key를 통한 MemesIdList 반환") + void Given_ExistKey_When_findTopTenMemesLikeCountForWeek_Then_MemesIdList() { + // given + key += POSTFIX_WEEK; + rankingZSet.add(key, 7L, 7); + + for (int i = 0; i < 7; i++) { + LocalDate date = LocalDate.now().minusDays(i); + String dateKey = PREFIX + date; + rankingZSet.add(dateKey, (long)i, i); + } + + // when + List result = rankingService.findTopTenMemesLikeCountForWeek(); + + //then + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0)).isEqualTo(7L); + } } From 1a5fdfe4a3388aaf146348e906d1d71d222ca093 Mon Sep 17 00:00:00 2001 From: JunRain Date: Mon, 27 May 2024 23:02:16 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[#92]=20=EC=A3=BC=EA=B0=84=20=EC=9B=94?= =?UTF-8?q?=EA=B0=84=20=EC=B2=AB=20=ED=98=B8=EC=B6=9C=20=EC=8B=9C,=20sorte?= =?UTF-8?q?d=20set=EC=9D=98=20=ED=82=A4=EA=B0=92=EC=9D=84=20=ED=95=A9?= =?UTF-8?q?=EC=B9=98=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/memes/dto/MemesRankDto.java | 22 +++++ .../memes/dto/response/MemesInfoResponse.java | 11 +++ .../domain/memes/service/MemesService.java | 2 + .../domain/memes/service/RankingService.java | 86 +++++++++++++++---- .../memes/service/RankingServiceTest.java | 35 ++++---- 5 files changed, 122 insertions(+), 34 deletions(-) create mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/MemesRankDto.java diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/MemesRankDto.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/MemesRankDto.java new file mode 100644 index 00000000..9cfe2040 --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/MemesRankDto.java @@ -0,0 +1,22 @@ +package com.example.memetory.domain.memes.dto; + +import org.springframework.data.redis.core.ZSetOperations; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Builder +public class MemesRankDto { + private Long memesId; + private Double score; + + public static MemesRankDto of(ZSetOperations.TypedTuple zSet) { + return MemesRankDto.builder() + .memesId(zSet.getValue()) + .score(zSet.getScore()) + .build(); + } +} diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/response/MemesInfoResponse.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/response/MemesInfoResponse.java index 29142722..fe208cee 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/response/MemesInfoResponse.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/response/MemesInfoResponse.java @@ -55,4 +55,15 @@ public static MemesInfoResponse of(Memes memes) { .createdAt(memes.getCreatedAt()) .build(); } + + public static MemesInfoResponse fromMemesAndLikeCount(Memes memes, Long likeCount) { + return MemesInfoResponse.builder() + .memesId(memes.getId()) + .memberNickname(memes.getMember().getNickname()) + .title(memes.getTitle()) + .commentCount(memes.getCommentCount()) + .likeCount(likeCount) + .createdAt(memes.getCreatedAt()) + .build(); + } } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/MemesService.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/MemesService.java index 92244d36..473c73e8 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/MemesService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/MemesService.java @@ -105,4 +105,6 @@ public List findTopMemesByLikeForWeek() { return memesRepository.findTopMemesOrderByLikeCountForPeriod(now().minusWeeks(1)); } + //Todo memesIdList를 받아서 memesInfo로 순서 변환없이 변환하는 작업 필요 + } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java index 2974347b..bda34cfd 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/RankingService.java @@ -9,6 +9,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.example.memetory.domain.memes.dto.MemesRankDto; + import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -17,7 +19,7 @@ public class RankingService { private final String PREFIX = "LIKE_RANKING_DATE::"; private final String POSTFIX_WEEK = "::WEEK"; private final String POSTFIX_MONTH = "::MONTH"; - private final Long TOP_TEN = 10L; + private final Long TOP_TEN = 9L; private final ZSetOperations rankingZSet; @@ -25,26 +27,33 @@ public class RankingService { public void increaseTodayMemesLikeCountFromMemesId(Long memesId) { String key = PREFIX + LocalDate.now(); + increaseMemesLikeCountForToday(key, memesId); + increaseMemesLikeCountForLastWeek(key, memesId); + increaseMemesLikeCountForLastMonth(key, memesId); + } + + private void increaseMemesLikeCountForToday(String key, Long memesId) { rankingZSet.incrementScore(key, memesId, 1); } - // Todo 음수를 확인하는 로직 추가 필요해 보임 - @Transactional - public void decreaseTodayMemesLikeCountFromMemesId(Long memesId) { - String key = PREFIX + LocalDate.now(); + private void increaseMemesLikeCountForLastWeek(String key, Long memesId) { + key += POSTFIX_WEEK; - rankingZSet.incrementScore(key, memesId, -1); + unionMemesIfKeyNotExists(key, 7); + rankingZSet.incrementScore(key, memesId, 1); } - @Transactional(readOnly = true) - public List findTopTenMemesLikeCountForWeek() { - String key = PREFIX + LocalDate.now() + POSTFIX_WEEK; + private void increaseMemesLikeCountForLastMonth(String key, Long memesId) { + key += POSTFIX_MONTH; - if (isNotExistedKey(key)) - unionMemesFromKeyAndDay(key, 7); + unionMemesIfKeyNotExists(key, 30); + rankingZSet.incrementScore(key, memesId, 1); + } - Set result = rankingZSet.reverseRange(key, 0, TOP_TEN); - return List.copyOf(result); + private void unionMemesIfKeyNotExists(String key, int day) { + if (isNotExistedKey(key)) { + unionMemesFromKeyAndDay(key, day); + } } private boolean isNotExistedKey(String key) { @@ -65,15 +74,54 @@ private void unionMemesFromKeyAndDay(String key, int day) { rankingZSet.unionAndStore(key, keyList, key); } + @Transactional + public void decreaseTodayMemesLikeCountFromMemesId(Long memesId) { + String key = PREFIX + LocalDate.now(); + + decreaseMemesLikeCountForToday(key, memesId); + decreaseMemesLikeCountForLastWeek(key, memesId); + decreaseMemesLikeCountForLastMonth(key, memesId); + } + + private void decreaseMemesLikeCountForToday(String key, Long memesId) { + rankingZSet.incrementScore(key, memesId, -1); + } + + private void decreaseMemesLikeCountForLastWeek(String key, Long memesId) { + key += POSTFIX_WEEK; + + unionMemesIfKeyNotExists(key, 7); + rankingZSet.incrementScore(key, memesId, -1); + } + + private void decreaseMemesLikeCountForLastMonth(String key, Long memesId) { + key += POSTFIX_MONTH; + + unionMemesIfKeyNotExists(key, 30); + rankingZSet.incrementScore(key, memesId, -1); + } + @Transactional(readOnly = true) - public List findTopTenMemesLikeCountForMonth() { + public List findTopTenMemesLikeCountForWeek() { + String key = PREFIX + LocalDate.now() + POSTFIX_WEEK; + + unionMemesIfKeyNotExists(key, 7); + + Set> rankTuple = rankingZSet.reverseRangeWithScores(key, 0, TOP_TEN); + List result = rankTuple.stream().map(MemesRankDto::of).toList(); + + return result; + } + + @Transactional(readOnly = true) + public List findTopTenMemesLikeCountForMonth() { String key = PREFIX + LocalDate.now() + POSTFIX_MONTH; - if (isNotExistedKey(key)) { - unionMemesFromKeyAndDay(key, 30); - } + unionMemesIfKeyNotExists(key, 30); + + Set> rankTuple = rankingZSet.reverseRangeWithScores(key, 0, TOP_TEN); + List result = rankTuple.stream().map(MemesRankDto::of).toList(); - Set result = rankingZSet.reverseRange(key, 0, TOP_TEN); - return List.copyOf(result); + return result; } } diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java index 6e77c49c..e7558a85 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/RankingServiceTest.java @@ -14,11 +14,14 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.ZSetOperations; +import com.example.memetory.domain.memes.dto.MemesRankDto; + @DisplayName("Ranking 서비스 테스트의 ") @SpringBootTest public class RankingServiceTest { private final String PREFIX = "LIKE_RANKING_DATE::"; private final String POSTFIX_WEEK = "::WEEK"; + private final String POSTFIX_MONTH = "::MONTH"; private final Long MEMES_ID = -1L; private String key; @@ -32,6 +35,7 @@ public class RankingServiceTest { @BeforeEach public void setUp() { + redisConnectionFactory.getConnection().flushAll(); key = PREFIX + LocalDate.now(); } @@ -42,7 +46,7 @@ void clearRedis() { @Test @DisplayName("존재하지 않는 memes와 memesId를 통한 score 증가 성공") - void Given_memesId_When_increaseTodayMemesLikeCountFromMemesId_Then_Memems_Score() { + void Given_memesId_When_increaseTodayMemesLikeCountFromMemesId_Then_Memes_Score() { // given Double expectedScore = 1.0; @@ -51,6 +55,8 @@ void Given_memesId_When_increaseTodayMemesLikeCountFromMemesId_Then_Memems_Score // then assertThat(rankingZSet.score(key, MEMES_ID)).isEqualTo(expectedScore); + assertThat(rankingZSet.score(key + POSTFIX_WEEK, MEMES_ID)).isEqualTo(expectedScore); + assertThat(rankingZSet.score(key + POSTFIX_MONTH, MEMES_ID)).isEqualTo(expectedScore); } @Test @@ -59,48 +65,47 @@ void Given_existMemesAndMemesId_When_increaseTodayMemesLikeCountFromMemesId_Then // given Double expectedScore = 2.0; - rankingZSet.add(key, MEMES_ID, 1); - // when rankingService.increaseTodayMemesLikeCountFromMemesId(MEMES_ID); + rankingService.increaseTodayMemesLikeCountFromMemesId(MEMES_ID); // then assertThat(rankingZSet.score(key, MEMES_ID)).isEqualTo(expectedScore); + assertThat(rankingZSet.score(key + POSTFIX_WEEK, MEMES_ID)).isEqualTo(expectedScore); + assertThat(rankingZSet.score(key + POSTFIX_MONTH, MEMES_ID)).isEqualTo(expectedScore); } @Test - @DisplayName("존재하는 memes와 memesId를 통한 score 증가") + @DisplayName("존재하는 memes와 memesId를 통한 score 감소") void Given_existMemesAndMemesId_When_decreaseTodayMemesLikeCountFromMemesId_Then_Memes_Score() { // given - Double expectedScore = 1.0; - - rankingZSet.add(key, MEMES_ID, 2); + Double expectedScore = -1.0; // when rankingService.decreaseTodayMemesLikeCountFromMemesId(MEMES_ID); // then assertThat(rankingZSet.score(key, MEMES_ID)).isEqualTo(expectedScore); + assertThat(rankingZSet.score(key + POSTFIX_WEEK, MEMES_ID)).isEqualTo(expectedScore); + assertThat(rankingZSet.score(key + POSTFIX_MONTH, MEMES_ID)).isEqualTo(expectedScore); } @Test @DisplayName("존재하지 않는 주간 key를 통한 MemesIdList 반환") void Given_NotExistKey_When_findTopTenMemesLikeCountForWeek_Then_MemesIdList() { // given - final int DAY = 7; - - for (int i = 0; i < DAY; i++) { + for (int i = 0; i < 7; i++) { LocalDate date = LocalDate.now().minusDays(i); String dateKey = PREFIX + date; rankingZSet.add(dateKey, (long)i, i); } // when - List result = rankingService.findTopTenMemesLikeCountForWeek(); + List result = rankingService.findTopTenMemesLikeCountForWeek(); //then - assertThat(result.size()).isEqualTo(DAY - 1); - assertThat(result.get(0)).isEqualTo(DAY - 1); + assertThat(result.size()).isEqualTo(6); + assertThat(result.get(0).getMemesId()).isEqualTo(6); } @Test @@ -117,10 +122,10 @@ void Given_ExistKey_When_findTopTenMemesLikeCountForWeek_Then_MemesIdList() { } // when - List result = rankingService.findTopTenMemesLikeCountForWeek(); + List result = rankingService.findTopTenMemesLikeCountForWeek(); //then assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0)).isEqualTo(7L); + assertThat(result.get(0).getMemesId()).isEqualTo(7L); } } From ea67141ef54b4ead2b6fac94be83fdea4bffad20 Mon Sep 17 00:00:00 2001 From: JunRain Date: Mon, 27 May 2024 23:21:33 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[#92]=20likeService=EC=97=90=20rankingServi?= =?UTF-8?q?ce=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/domain/like/dto/LikeServiceDto.java | 7 ------- .../memetory/domain/like/entity/Like.java | 7 +++++++ .../domain/like/service/LikeService.java | 16 ++++++++++++++-- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/like/dto/LikeServiceDto.java b/backend/memetory/src/main/java/com/example/memetory/domain/like/dto/LikeServiceDto.java index 0d7edfed..9eb0619a 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/like/dto/LikeServiceDto.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/like/dto/LikeServiceDto.java @@ -20,11 +20,4 @@ public static LikeServiceDto fromEmailAndMemesId(String email, Long memesId) { .memesId(memesId) .build(); } - - public Like toEntityFromMemberAndMemes(Member member, Memes memes) { - return Like.builder() - .member(member) - .memes(memes) - .build(); - } } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/like/entity/Like.java b/backend/memetory/src/main/java/com/example/memetory/domain/like/entity/Like.java index 2ac515f0..b5b0c499 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/like/entity/Like.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/like/entity/Like.java @@ -52,4 +52,11 @@ public Like(Member member, Memes memes) { this.member = member; this.memes = memes; } + + public static Like fromMemberAndMemes(Member member, Memes memes) { + return Like.builder() + .member(member) + .memes(memes) + .build(); + } } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/like/service/LikeService.java b/backend/memetory/src/main/java/com/example/memetory/domain/like/service/LikeService.java index 3960e3cb..0c2b9291 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/like/service/LikeService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/like/service/LikeService.java @@ -13,6 +13,7 @@ import com.example.memetory.domain.member.service.MemberService; import com.example.memetory.domain.memes.entity.Memes; import com.example.memetory.domain.memes.service.MemesService; +import com.example.memetory.domain.memes.service.RankingService; import lombok.RequiredArgsConstructor; @@ -22,15 +23,16 @@ public class LikeService { private final MemberService memberService; private final MemesService memesService; private final LikeRepository likeRepository; + private final RankingService rankingService; @Transactional public void registerLike(LikeServiceDto likeServiceDto) { Member member = memberService.findMemberFromEmail(likeServiceDto.getEmail()); Memes memes = memesService.findMemesFromMemesId(likeServiceDto.getMemesId()); - Like newLike = likeServiceDto.toEntityFromMemberAndMemes(member, memes); + Like newLike = Like.fromMemberAndMemes(member, memes); saveLike(newLike); - memes.addLikeCount(); + increaseMemesLikeCount(memes); } private void saveLike(Like like) { @@ -41,6 +43,11 @@ private void saveLike(Like like) { } } + private void increaseMemesLikeCount(Memes memes) { + memes.addLikeCount(); + rankingService.increaseTodayMemesLikeCountFromMemesId(memes.getId()); + } + @Transactional public void cancelLike(LikeServiceDto likeServiceDto) { Member member = memberService.findMemberFromEmail(likeServiceDto.getEmail()); @@ -48,6 +55,11 @@ public void cancelLike(LikeServiceDto likeServiceDto) { Like like = likeRepository.findLikeByMemberAndMemes(member, memes).orElseThrow(NotFoundLikeException::new); likeRepository.delete(like); + decreaseMemesLikeCount(memes); + } + + private void decreaseMemesLikeCount(Memes memes) { memes.cancelLikeCount(); + rankingService.decreaseTodayMemesLikeCountFromMemesId(memes.getId()); } } From ef13c2bd10c986b034ab1749717f411745550574 Mon Sep 17 00:00:00 2001 From: JunRain Date: Mon, 27 May 2024 23:31:09 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[#92]=20likeService=EC=97=90=20rankingServi?= =?UTF-8?q?ce=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B8=ED=95=9C=20Test=20=EC=BD=94=EB=93=9C=EC=97=90=20ra?= =?UTF-8?q?nkingService=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/like/dto/LikeServiceDto.java | 19 ++++++++----------- .../domain/like/service/LikeServiceTest.java | 6 ++++++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/like/dto/LikeServiceDto.java b/backend/memetory/src/main/java/com/example/memetory/domain/like/dto/LikeServiceDto.java index 9eb0619a..df38afe5 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/like/dto/LikeServiceDto.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/like/dto/LikeServiceDto.java @@ -1,8 +1,5 @@ package com.example.memetory.domain.like.dto; -import com.example.memetory.domain.like.entity.Like; -import com.example.memetory.domain.member.entity.Member; -import com.example.memetory.domain.memes.entity.Memes; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -11,13 +8,13 @@ @Builder @AllArgsConstructor public class LikeServiceDto { - private Long memesId; - private String email; + private Long memesId; + private String email; - public static LikeServiceDto fromEmailAndMemesId(String email, Long memesId) { - return LikeServiceDto.builder() - .email(email) - .memesId(memesId) - .build(); - } + public static LikeServiceDto fromEmailAndMemesId(String email, Long memesId) { + return LikeServiceDto.builder() + .email(email) + .memesId(memesId) + .build(); + } } diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/like/service/LikeServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/like/service/LikeServiceTest.java index 4c3615f0..9598a269 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/like/service/LikeServiceTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/like/service/LikeServiceTest.java @@ -26,7 +26,9 @@ import com.example.memetory.domain.member.entity.Member; import com.example.memetory.domain.member.service.MemberService; import com.example.memetory.domain.memes.entity.Memes; +import com.example.memetory.domain.memes.repository.MemesRepository; import com.example.memetory.domain.memes.service.MemesService; +import com.example.memetory.domain.memes.service.RankingService; @DisplayName("Like 서비스 테스트의 ") @ExtendWith(MockitoExtension.class) @@ -37,6 +39,8 @@ public class LikeServiceTest { private MemesService memesService; @Mock private LikeRepository likeRepository; + @Mock + private RankingService rankingService; @InjectMocks private LikeService likeService; @@ -63,6 +67,7 @@ void Given_likeServiceDto_When_registerLike_Execute_likeRepository_save() { // then assertThat(memes.getLikeCount()).isEqualTo(2L); + verify(rankingService).increaseTodayMemesLikeCountFromMemesId(any()); verify(likeRepository).save(any(Like.class)); } @@ -95,5 +100,6 @@ void Given_LikeServiceDto_when_cancelLike_Execute_likeRepository_delete() { // then assertThat(memes.getLikeCount()).isZero(); verify(likeRepository).delete(like); + verify(rankingService).decreaseTodayMemesLikeCountFromMemesId(any()); } } From 8a29d4bba5ffe4021d295b2a43c1575ce072c324 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 28 May 2024 00:10:50 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[#92]=20memesService=EC=97=90=20RankingServ?= =?UTF-8?q?ice=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/memes/dto/MemesRankDto.java | 4 +-- .../domain/memes/service/MemesService.java | 22 ++++++++++--- .../memes/service/MemesServiceTest.java | 31 +++++++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/MemesRankDto.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/MemesRankDto.java index 9cfe2040..9d08fdc7 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/MemesRankDto.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/dto/MemesRankDto.java @@ -11,12 +11,12 @@ @Builder public class MemesRankDto { private Long memesId; - private Double score; + private Long score; public static MemesRankDto of(ZSetOperations.TypedTuple zSet) { return MemesRankDto.builder() .memesId(zSet.getValue()) - .score(zSet.getScore()) + .score(zSet.getScore().longValue()) .build(); } } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/MemesService.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/MemesService.java index 473c73e8..1dc480c2 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/MemesService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/service/MemesService.java @@ -1,7 +1,5 @@ package com.example.memetory.domain.memes.service; -import static java.time.LocalDateTime.*; - import java.util.List; import org.springframework.data.domain.Pageable; @@ -13,6 +11,7 @@ import com.example.memetory.domain.member.service.MemberService; import com.example.memetory.domain.meme.entity.Meme; import com.example.memetory.domain.meme.service.MemeService; +import com.example.memetory.domain.memes.dto.MemesRankDto; import com.example.memetory.domain.memes.dto.MemesServiceDto; import com.example.memetory.domain.memes.dto.response.MemesInfoResponse; import com.example.memetory.domain.memes.dto.response.MemesInfoSliceResponse; @@ -30,6 +29,7 @@ public class MemesService { private final MemberService memberService; private final MemeService memeService; private final MemesRepository memesRepository; + private final RankingService rankingService; @Transactional public MemesResponse registerMemes(MemesServiceDto memesServiceDto) { @@ -97,14 +97,26 @@ public List findTopMemesByLike() { @Transactional(readOnly = true) public List findTopMemesByLikeForMonth() { - return memesRepository.findTopMemesOrderByLikeCountForPeriod(now().minusMonths(1)); + List memesRankDtoList = rankingService.findTopTenMemesLikeCountForMonth(); + + return convertMemesRankDtoListIntoMemesInfoResponseList(memesRankDtoList); } @Transactional(readOnly = true) public List findTopMemesByLikeForWeek() { - return memesRepository.findTopMemesOrderByLikeCountForPeriod(now().minusWeeks(1)); + List memesRankDtoList = rankingService.findTopTenMemesLikeCountForWeek(); + + return convertMemesRankDtoListIntoMemesInfoResponseList(memesRankDtoList); } - //Todo memesIdList를 받아서 memesInfo로 순서 변환없이 변환하는 작업 필요 + private List convertMemesRankDtoListIntoMemesInfoResponseList( + List memesRankDtoList) { + return memesRankDtoList.stream().map(this::convertMemesRankDtoInooMemesInfoResponse).toList(); + } + + private MemesInfoResponse convertMemesRankDtoInooMemesInfoResponse(MemesRankDto memesRank) { + Memes memes = findMemesFromMemesId(memesRank.getMemesId()); + return MemesInfoResponse.fromMemesAndLikeCount(memes, memesRank.getScore()); + } } diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/MemesServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/MemesServiceTest.java index e6623f5e..d6fe1370 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/MemesServiceTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/memes/service/MemesServiceTest.java @@ -7,6 +7,8 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.BDDMockito.*; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -22,7 +24,9 @@ import com.example.memetory.domain.meme.entity.Meme; import com.example.memetory.domain.meme.exception.AccessDeniedMemeException; import com.example.memetory.domain.meme.service.MemeService; +import com.example.memetory.domain.memes.dto.MemesRankDto; import com.example.memetory.domain.memes.dto.MemesServiceDto; +import com.example.memetory.domain.memes.dto.response.MemesInfoResponse; import com.example.memetory.domain.memes.dto.response.MemesResponse; import com.example.memetory.domain.memes.entity.Memes; import com.example.memetory.domain.memes.exception.AccessDinedMemesException; @@ -40,6 +44,8 @@ public class MemesServiceTest { private MemesRepository memesRepository; @Mock private MemeService memeService; + @Mock + private RankingService rankingService; private MemesServiceDto memesServiceDto; private Member member; @@ -138,4 +144,29 @@ void Given_notExistMemesId_When_findMemesResponse_Throw_MemesResponse() { // then assertThrows(NotFoundMemesException.class, () -> memesService.findMemesResponse(memesServiceDto)); } + + @Test + @DisplayName("주간 탑 10 조회로 인한 List 반환") + void When_findTopMemesByLikeForWeek_Then_List_MemesInfoResponse() { + // given + List memesRankDtoList = generateMemesRankDtoList(); + given(rankingService.findTopTenMemesLikeCountForWeek()).willReturn(memesRankDtoList); + given(memesRepository.findByMemesId(any())).willReturn(Optional.ofNullable(MEMES(member, meme))); + + // when + List result = memesService.findTopMemesByLikeForWeek(); + + // then + assertThat(result).hasSize(10); + assertThat(result.get(0)).isInstanceOf(MemesInfoResponse.class); + } + + private List generateMemesRankDtoList() { + List expectedResult = new ArrayList<>(); + + for (long i = 1; i <= 10; i++) { + expectedResult.add(new MemesRankDto(i, i)); + } + return expectedResult; + } }