diff --git a/backend/techpick-api/src/main/java/techpick/api/application/ranking/controller/RankingApiController.java b/backend/techpick-api/src/main/java/techpick/api/application/ranking/controller/RankingApiController.java index e05743d70..52ecb75a0 100644 --- a/backend/techpick-api/src/main/java/techpick/api/application/ranking/controller/RankingApiController.java +++ b/backend/techpick-api/src/main/java/techpick/api/application/ranking/controller/RankingApiController.java @@ -1,6 +1,9 @@ package techpick.api.application.ranking.controller; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -13,8 +16,13 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import techpick.api.infrastructure.ranking.RankingRepository; +import techpick.api.application.ranking.dto.LinkInfoWithViewCount; +import techpick.api.application.ranking.dto.RankingApiMapper; +import techpick.api.domain.link.exception.ApiLinkException; +import techpick.api.domain.link.service.LinkService; +import techpick.api.infrastructure.ranking.RankingApi; import techpick.api.application.ranking.dto.RankingByViewCount; +import techpick.core.dto.UrlWithCount; /** * techpick-ranking 서버로 부터 데이터를 받아와 뿌려준다. @@ -26,7 +34,9 @@ @Tag(name = "추천/소개 API", description = "링크, 픽 등에 대한 소개") public class RankingApiController { - private final RankingRepository rankingRepository; + private final RankingApi rankingApi; + private final RankingApiMapper rankingApiMapper; + private final LinkService linkService; /** * 주별, 일별 조회 수를 기반 으로 추천 한다. @@ -54,13 +64,33 @@ public ResponseEntity getSuggestionByViewCount( var before30Days = currentDay.minusDays(30); var dailyViewRanking = // 오늘 + 어제 - rankingRepository.getLinkRankingByViewCount(before1Day, currentDay, LIMIT).getBody(); + mapToLinkInfoRanking(rankingApi.getUrlRankingByViewCount(before1Day, currentDay, LIMIT).getBody()); + var past7DaysViewRanking = // 일주일 전 ~ 어제 - rankingRepository.getLinkRankingByViewCount(before7Days, before1Day, LIMIT).getBody(); + mapToLinkInfoRanking(rankingApi.getUrlRankingByViewCount(before7Days, before1Day, LIMIT).getBody()); + var past30DaysPickRanking = // 한달 전 ~ 어제 - rankingRepository.getLinkRankingByPickedCount(before30Days, before1Day, LIMIT).getBody(); + mapToLinkInfoRanking( + rankingApi.getUrlRankingByPickedCount(before30Days, before1Day, LIMIT).getBody()); var response = new RankingByViewCount(dailyViewRanking, past7DaysViewRanking, past30DaysPickRanking); return ResponseEntity.ok(response); } + + private List mapToLinkInfoRanking(List urlWithCountList) { + if (Objects.isNull(urlWithCountList)) { + return List.of(/* empty list */); + } + var result = new ArrayList(); + for (UrlWithCount urlWithCount : urlWithCountList) { + try { + var linkInfo = linkService.getLinkInfo(urlWithCount.url()); + var rankingInfo = rankingApiMapper.toRankingWithLinkInfo(urlWithCount, linkInfo); + result.add(rankingInfo); + } catch (ApiLinkException exception) { + log.error("[랭킹 획득 - 서버에 저장되지 않은 링크가 랭킹에 포함되어 있습니다! ={}", urlWithCount.url(), exception); + } + } + return result; + } } diff --git a/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/LinkInfoWithViewCount.java b/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/LinkInfoWithViewCount.java new file mode 100644 index 000000000..b3899e882 --- /dev/null +++ b/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/LinkInfoWithViewCount.java @@ -0,0 +1,16 @@ +package techpick.api.application.ranking.dto; + +import jakarta.validation.constraints.NotNull; +import techpick.core.dto.UrlWithCount; + +/** + * 랭킹 서버로 부터 얻은 URL 정보 {@link UrlWithCount} 에 Opengraph Tag를 추가한 DTO + */ +public record LinkInfoWithViewCount( + @NotNull String url, + String title, + String description, + String imageUrl, + Long count +) { +} diff --git a/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/RankingApiMapper.java b/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/RankingApiMapper.java new file mode 100644 index 000000000..bfdfd4fe2 --- /dev/null +++ b/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/RankingApiMapper.java @@ -0,0 +1,20 @@ +package techpick.api.application.ranking.dto; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +import techpick.api.domain.link.dto.LinkInfo; +import techpick.core.dto.UrlWithCount; + +@Mapper( + componentModel = "spring", + injectionStrategy = InjectionStrategy.CONSTRUCTOR, + unmappedTargetPolicy = ReportingPolicy.ERROR +) +public interface RankingApiMapper { + + @Mapping(target = "url", source = "urlWithCount.url") + LinkInfoWithViewCount toRankingWithLinkInfo(UrlWithCount urlWithCount, LinkInfo linkInfo); +} diff --git a/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/RankingByViewCount.java b/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/RankingByViewCount.java index e00dd1243..6da83b68a 100644 --- a/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/RankingByViewCount.java +++ b/backend/techpick-api/src/main/java/techpick/api/application/ranking/dto/RankingByViewCount.java @@ -3,16 +3,15 @@ import java.util.List; import io.swagger.v3.oas.annotations.media.Schema; -import techpick.core.dto.UrlWithCount; public record RankingByViewCount( @Schema(description = "오늘 하루 동안 인기 있는 링크 Top 10") - List dailyViewRanking, + List dailyViewRanking, @Schema(description = "지난 7일동안 링크 조회 수 Top 10") - List weeklyViewRanking, + List weeklyViewRanking, @Schema(description = "지난 30일동안 링크가 픽된 횟수 Top 10") - List monthlyPickRanking + List monthlyPickRanking ) { } diff --git a/backend/techpick-api/src/main/java/techpick/api/config/HttpApiConfiguration.java b/backend/techpick-api/src/main/java/techpick/api/config/HttpApiConfiguration.java index 747ef24a6..7515af996 100644 --- a/backend/techpick-api/src/main/java/techpick/api/config/HttpApiConfiguration.java +++ b/backend/techpick-api/src/main/java/techpick/api/config/HttpApiConfiguration.java @@ -7,7 +7,7 @@ import org.springframework.web.client.support.RestClientAdapter; import org.springframework.web.service.invoker.HttpServiceProxyFactory; -import techpick.api.infrastructure.ranking.RankingRepository; +import techpick.api.infrastructure.ranking.RankingApi; /** * 외부 서버와 통신하는 것을 Http Interface 방식으로 사용하기 위한 설정.
@@ -20,10 +20,10 @@ public class HttpApiConfiguration { private String rankingServerUrl; @Bean - public RankingRepository rankingApi() { + public RankingApi rankingApi() { var restClient = RestClient.create(rankingServerUrl); var adapter = RestClientAdapter.create(restClient); var proxy = HttpServiceProxyFactory.builderFor(adapter).build(); - return proxy.createClient(RankingRepository.class); + return proxy.createClient(RankingApi.class); } } diff --git a/backend/techpick-api/src/main/java/techpick/api/domain/user/service/RankingBasedStrategy.java b/backend/techpick-api/src/main/java/techpick/api/domain/user/service/RankingBasedStrategy.java index 3998c57ad..152ce2944 100644 --- a/backend/techpick-api/src/main/java/techpick/api/domain/user/service/RankingBasedStrategy.java +++ b/backend/techpick-api/src/main/java/techpick/api/domain/user/service/RankingBasedStrategy.java @@ -7,16 +7,14 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import techpick.api.domain.link.dto.LinkMapper; import techpick.api.domain.link.exception.ApiLinkException; import techpick.api.domain.link.service.LinkService; import techpick.api.domain.pick.dto.PickCommand; import techpick.api.domain.pick.service.PickService; -import techpick.api.infrastructure.ranking.RankingRepository; +import techpick.api.infrastructure.ranking.RankingApi; import techpick.core.dto.UrlWithCount; import techpick.core.model.folder.Folder; import techpick.core.model.folder.FolderRepository; @@ -34,7 +32,7 @@ public class RankingBasedStrategy implements InitialFolderStrategy { private static final Integer LOAD_LIMIT = 15; private final FolderRepository folderRepository; - private final RankingRepository rankingRepository; + private final RankingApi rankingApi; private final PickService pickService; private final LinkService linkService; @@ -44,8 +42,8 @@ public void initFolder(User user, Folder root) { var currentDay = LocalDate.now(); var before1Day = currentDay.minusDays(1); var before30Days = currentDay.minusDays(30); - var monthlyRanking = rankingRepository - .getLinkRankingByViewCount(before30Days, before1Day, LOAD_LIMIT) + var monthlyRanking = rankingApi + .getUrlRankingByViewCount(before30Days, before1Day, LOAD_LIMIT) .getBody(); savePickFromRankingList(user.getId(), monthlyRanking, monthlyFolder.getId()); } diff --git a/backend/techpick-api/src/main/java/techpick/api/infrastructure/ranking/RankingRepository.java b/backend/techpick-api/src/main/java/techpick/api/infrastructure/ranking/RankingApi.java similarity index 87% rename from backend/techpick-api/src/main/java/techpick/api/infrastructure/ranking/RankingRepository.java rename to backend/techpick-api/src/main/java/techpick/api/infrastructure/ranking/RankingApi.java index 5c294a364..98b0b4a52 100644 --- a/backend/techpick-api/src/main/java/techpick/api/infrastructure/ranking/RankingRepository.java +++ b/backend/techpick-api/src/main/java/techpick/api/infrastructure/ranking/RankingApi.java @@ -15,13 +15,13 @@ * 랭킹 서버와 통신하기 위한 Http Interface.
* 형식은 techpick-api 모듈의 컨트롤러와 일치합니다. */ -public interface RankingRepository { +public interface RankingApi { /** * 조회수 기반 링크 랭킹 */ @GetExchange("/ranking/link/view") - ResponseEntity> getLinkRankingByViewCount( + ResponseEntity> getUrlRankingByViewCount( @RequestParam("date_begin") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate dateBegin, @RequestParam("date_end") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate dateEnd, @RequestParam(required = false, defaultValue = "5") Integer limit @@ -31,7 +31,7 @@ ResponseEntity> getLinkRankingByViewCount( * 픽된 횟수 기반 링크 랭킹 */ @GetExchange("/ranking/link/picked") - ResponseEntity> getLinkRankingByPickedCount( + ResponseEntity> getUrlRankingByPickedCount( @RequestParam("date_begin") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate dateBegin, @RequestParam("date_end") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate dateEnd, @RequestParam(required = false, defaultValue = "5") Integer limit