Skip to content

Commit

Permalink
Merge pull request #203 from bucket-back/OMCT-271-feed-ranking-redis
Browse files Browse the repository at this point in the history
[OMCT-271] 피드 랭킹 기능 추가
  • Loading branch information
Curry4182 authored Jan 2, 2024
2 parents badd8a1 + 517d046 commit 905d439
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import com.programmers.bucketback.domains.feed.api.request.FeedUpdateRequest;
import com.programmers.bucketback.domains.feed.api.response.FeedCreateResponse;
import com.programmers.bucketback.domains.feed.api.response.FeedGetByCursorResponse;
import com.programmers.bucketback.domains.feed.api.response.FeedGetRankingResponse;
import com.programmers.bucketback.domains.feed.api.response.FeedGetResponse;
import com.programmers.bucketback.domains.feed.application.FeedService;
import com.programmers.bucketback.domains.feed.application.dto.response.FeedGetRankingServiceResponse;
import com.programmers.bucketback.domains.feed.application.dto.response.FeedGetServiceResponse;
import com.programmers.bucketback.domains.feed.model.FeedCursorSummaryLike;
import com.programmers.bucketback.global.cursor.CursorRequest;
Expand Down Expand Up @@ -111,4 +113,13 @@ public ResponseEntity<FeedGetResponse> getFeed(@PathVariable final Long feedId)

return ResponseEntity.ok(response);
}

@Operation(summary = "피드 랭킹 조회", description = "피드의 랭킹을 조회한다.")
@GetMapping("/ranking")
public ResponseEntity<FeedGetRankingResponse> getFeedRanking() {
FeedGetRankingServiceResponse serviceResponse = feedService.getFeedRanking();
FeedGetRankingResponse response = FeedGetRankingResponse.from(serviceResponse);

return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.programmers.bucketback.domains.feed.api.response;

import java.util.List;

import com.programmers.bucketback.domains.feed.application.dto.response.FeedGetRankingServiceResponse;
import com.programmers.bucketback.redis.feed.model.FeedRankingInfo;

public record FeedGetRankingResponse(
List<FeedRankingInfo> feedRankingInfos
) {

public static FeedGetRankingResponse from(final FeedGetRankingServiceResponse serviceResponse) {
List<FeedRankingInfo> feedRankingInfos = serviceResponse.feedRankingInfos();

return new FeedGetRankingResponse(feedRankingInfos);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.programmers.bucketback.domains.feed.application;

import java.util.List;

import org.springframework.stereotype.Service;

import com.programmers.bucketback.common.cursor.CursorPageParameters;
import com.programmers.bucketback.common.cursor.CursorSummary;
import com.programmers.bucketback.common.model.Hobby;
import com.programmers.bucketback.domains.feed.application.dto.response.FeedGetRankingServiceResponse;
import com.programmers.bucketback.domains.feed.application.dto.response.FeedGetServiceResponse;
import com.programmers.bucketback.domains.feed.implementation.FeedAppender;
import com.programmers.bucketback.domains.feed.implementation.FeedCursorReader;
Expand All @@ -14,11 +17,14 @@
import com.programmers.bucketback.domains.feed.model.FeedCreateServiceRequest;
import com.programmers.bucketback.domains.feed.model.FeedCursorSummaryLike;
import com.programmers.bucketback.domains.feed.model.FeedDetail;
import com.programmers.bucketback.domains.feed.model.FeedInfo;
import com.programmers.bucketback.domains.feed.model.FeedSortCondition;
import com.programmers.bucketback.domains.feed.model.FeedUpdateServiceRequest;
import com.programmers.bucketback.error.BusinessException;
import com.programmers.bucketback.error.ErrorCode;
import com.programmers.bucketback.global.util.MemberUtils;
import com.programmers.bucketback.redis.feed.FeedRedisManager;
import com.programmers.bucketback.redis.feed.model.FeedRankingInfo;

import lombok.RequiredArgsConstructor;

Expand All @@ -32,6 +38,7 @@ public class FeedService {
private final FeedRemover feedRemover;
private final FeedCursorReader feedCursorReader;
private final MemberUtils memberUtils;
private final FeedRedisManager feedRedisManager;

/** 피드 생성 */
public Long createFeed(final FeedCreateServiceRequest request) {
Expand Down Expand Up @@ -83,19 +90,58 @@ public void deleteFeed(final Long feedId) {
public void likeFeed(final Long feedId) {
Long memberId = memberUtils.getCurrentMemberId();
feedAppender.like(memberId, feedId);
changePopularity(feedId, 1);
}

/** 피드 좋아요 취소 */
public void unLikeFeed(final Long feedId) {
Long memberId = memberUtils.getCurrentMemberId();
feedRemover.unlike(memberId, feedId);
changePopularity(feedId, -1);
}

/** 피드 상세 조회 **/
public FeedGetServiceResponse getFeed(final Long feedId) {
final Long memberId = memberUtils.getCurrentMemberId();
final FeedDetail detail = feedReader.readDetail(feedId, memberId);
changePopularity(detail, 1);

return FeedGetServiceResponse.from(detail);
}

private void changePopularity(
final Long feedId,
final int value
) {
if (feedRedisManager.isFeedExist(feedId)) {
feedRedisManager.changePopularity(feedId, value);
return;
}

Long memberId = memberUtils.getCurrentMemberId();
changePopularity(feedReader.readDetail(feedId, memberId), value);
}

private void changePopularity(
final FeedDetail feedDetail,
final int value
) {
FeedInfo feedInfo = feedDetail.feedInfo();

FeedRankingInfo feedRankingInfo = FeedRankingInfo.builder()
.feedId(feedInfo.id())
.feedContent(feedInfo.content())
.hobbyName(feedInfo.hobby())
.bucketName(feedInfo.bucketName())
.likeCount(feedInfo.likeCount())
.build();

feedRedisManager.changePopularity(feedRankingInfo, value);
}

public FeedGetRankingServiceResponse getFeedRanking() {
List<FeedRankingInfo> feedRankingInfos = feedRedisManager.getFeedRanking();

return new FeedGetRankingServiceResponse(feedRankingInfos);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.programmers.bucketback.domains.feed.application.dto.response;

import java.util.List;

import com.programmers.bucketback.redis.feed.model.FeedRankingInfo;

public record FeedGetRankingServiceResponse(
List<FeedRankingInfo> feedRankingInfos
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public RedisConnectionFactory redisConnectionFactory() {
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.programmers.bucketback.redis.feed;

import java.util.List;
import java.util.Set;

import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import com.programmers.bucketback.redis.feed.model.FeedRankingInfo;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class FeedRedisManager {

private static final String FEED_RANKING_INFO_HASH_KEY = "feedRankingInfoHashKey";
private static final String FEED_RANKING_INFO_SET_KEY = "feedRankingInfoSetKey";

private final RedisTemplate<String, Object> redisTemplate;

public List<FeedRankingInfo> getFeedRanking() {
ZSetOperations<String, Object> ZSetOperations = redisTemplate.opsForZSet();

Set<ZSetOperations.TypedTuple<Object>> feedRankingInfoSet =
ZSetOperations.reverseRangeWithScores(
FEED_RANKING_INFO_SET_KEY, 0, 9
);

return feedRankingInfoSet.stream()
.map(FeedRankingInfo::of)
.toList();
}

public boolean isFeedExist(final Long feedId) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();

return hash.hasKey(FEED_RANKING_INFO_HASH_KEY, feedId);
}

public void changePopularity(final FeedRankingInfo feedRankingInfo, final int value) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put(FEED_RANKING_INFO_HASH_KEY, feedRankingInfo.feedId(), feedRankingInfo);

redisTemplate.opsForZSet().incrementScore(FEED_RANKING_INFO_SET_KEY, feedRankingInfo, value);
}

public void changePopularity(final Long feedId, final int value) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();

if (!hash.hasKey(FEED_RANKING_INFO_HASH_KEY, feedId)) {
return;
}

FeedRankingInfo feedRankingInfo = (FeedRankingInfo)hash.get(FEED_RANKING_INFO_HASH_KEY, feedId);
changePopularity(feedRankingInfo, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.programmers.bucketback.redis.feed.model;

import org.springframework.data.redis.core.ZSetOperations;

import lombok.Builder;

@Builder
public record FeedRankingInfo(

Long feedId,
String hobbyName,
String feedContent,
String bucketName,
int likeCount

) {
public static FeedRankingInfo of(
final ZSetOperations.TypedTuple<Object> objectTypedTuple
) {
FeedRankingInfo response = (FeedRankingInfo)objectTypedTuple.getValue();

return FeedRankingInfo.builder()
.feedId(response.feedId())
.hobbyName(response.hobbyName())
.feedContent(response.feedContent())
.bucketName(response.bucketName())
.likeCount(response.likeCount())
.build();
}
}

0 comments on commit 905d439

Please sign in to comment.