Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/docs/asciidoc/post-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,18 @@ include::{snippetsDir}/loadUserPickPopular/1/http-response.adoc[]
==== Response Body Fields
include::{snippetsDir}/loadUserPickPopular/1/response-fields.adoc[]

---


=== **10. 그루밍 라운지 - "그루밍 바이블" 조회**

==== Request
include::{snippetsDir}/loadUserPickBible/1/http-request.adoc[]

==== 성공 Response
include::{snippetsDir}/loadUserPickBible/1/http-response.adoc[]

==== Response Body Fields
include::{snippetsDir}/loadUserPickBible/1/response-fields.adoc[]

---
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.ftm.server.adapter.in.web.post.controller;

import com.ftm.server.adapter.in.web.post.dto.response.GetUserPickPopularPostsResponse;
import com.ftm.server.application.port.in.user.GetUserPickPopularPostsUseCase;
import com.ftm.server.application.port.in.post.GetUserPickPopularPostsUseCase;
import com.ftm.server.common.response.ApiResponse;
import com.ftm.server.common.response.enums.SuccessResponseCode;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.ftm.server.adapter.in.web.post.controller;

import com.ftm.server.adapter.in.web.post.dto.response.LoadUserPickBiblePostsResponse;
import com.ftm.server.application.port.in.post.LoadUserPickBiblePostsUseCase;
import com.ftm.server.common.response.ApiResponse;
import com.ftm.server.common.response.enums.SuccessResponseCode;
import java.util.List;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@AllArgsConstructor
public class LoadUserPickBiblePostsController {

private final LoadUserPickBiblePostsUseCase userPickBiblePostsUseCase;

@GetMapping("/api/posts/userpick/bible")
public ResponseEntity<ApiResponse> loadUserPickGroomingBiblePosts() {
List<LoadUserPickBiblePostsResponse> result =
userPickBiblePostsUseCase.execute().stream()
.map(LoadUserPickBiblePostsResponse::from)
.toList();
return ResponseEntity.ok(ApiResponse.success(SuccessResponseCode.OK, result));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.ftm.server.adapter.in.web.post.dto.response;

import com.ftm.server.application.vo.post.UserPickBiblePostsVo;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class LoadUserPickBiblePostsResponse {
private final Integer ranking;
private final Long postId;
private final String title;
private final Long authorId;
private final String authorName;
private final Integer viewCount;
private final Integer likeCount;
private final Long scrapCount;
private final String imageUrl;
private final List<String> hashtags;

public static LoadUserPickBiblePostsResponse from(UserPickBiblePostsVo vo) {
return new LoadUserPickBiblePostsResponse(
vo.getRanking(),
vo.getPostId(),
vo.getTitle(),
vo.getAuthorId(),
vo.getAuthorName(),
vo.getViewCount(),
vo.getLikeCount(),
vo.getScrapCount(),
vo.getImageUrl(),
vo.getHashtags());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.ftm.server.adapter.out.cache;

import static com.ftm.server.common.consts.StaticConsts.*;

import com.ftm.server.application.port.out.cache.LoadUserPickBiblePostsWithCachePort;
import com.ftm.server.application.port.out.persistence.post.LoadPostPort;
import com.ftm.server.application.query.FindUserPickBiblePostsQuery;
import com.ftm.server.application.vo.post.PostWithIdAndAuthorVo;
import com.ftm.server.common.annotation.Adapter;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;

@Adapter
@RequiredArgsConstructor
public class LoadUserPickBiblePostsWithCacheAdapter implements LoadUserPickBiblePostsWithCachePort {

private final LoadPostPort loadPostPort;

@Override
@Cacheable(
cacheNames = USER_PICK_BIBLE_POSTS_CACHE_NAME,
key = USER_PICK_BIBLE_POSTS_CACHE_KEY_ALL)
public List<PostWithIdAndAuthorVo> getUserPickBiblePost() {
return execute();
}

@Override
@CachePut(cacheNames = USER_PICK_BIBLE_POSTS_CACHE_NAME, key = USER_PICK_BIBLE_POSTS_CACHE_NAME)
public List<PostWithIdAndAuthorVo> getUserPickBiblePostCachePut() {
return execute();
}

public List<PostWithIdAndAuthorVo> execute() {
// 최근 1개월 상위 4개 post id를 조회

List<PostWithIdAndAuthorVo> postList =
loadPostPort.loadUserPickBiblePosts(FindUserPickBiblePostsQuery.of(4));

if (postList.isEmpty()) return List.of();

return postList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import com.ftm.server.adapter.out.persistence.repository.*;
import com.ftm.server.application.port.out.persistence.post.*;
import com.ftm.server.application.query.*;
import com.ftm.server.application.vo.post.PostAndBookmarkCountVo;
import com.ftm.server.application.vo.post.PostWithBookmarkCountVo;
import com.ftm.server.application.vo.post.UserIdAndNameVo;
import com.ftm.server.application.vo.post.UserWithPostCountVo;
import com.ftm.server.application.vo.post.*;
import com.ftm.server.common.annotation.Adapter;
import com.ftm.server.common.exception.CustomException;
import com.ftm.server.common.response.enums.ErrorResponseCode;
Expand Down Expand Up @@ -160,6 +157,17 @@ public List<Post> loadUserPickPopularPosts(FindUserPickPopularPostsQuery query)
.toList();
}

@Override
public List<PostWithIdAndAuthorVo> loadUserPickBiblePosts(FindUserPickBiblePostsQuery query) {
return postRepository.findTopNPostsByLikeCount(query.getLimit());
}

@Override
public List<PostWithUserAndBookmarkCountVo> loadPostWithUserAndBookmarkCount(
FindByIdsQuery query) {
return postRepository.findAllPostsWithUserAndBookmarkCount(query);
}

@Override
public List<PostImage> loadPostImagesByPostId(FindByPostIdQuery query) {
PostJpaEntity postJpaEntity =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.ftm.server.adapter.out.persistence.model.PostJpaEntity;
import com.ftm.server.application.vo.post.PostAndBookmarkCountVo;
import com.ftm.server.application.vo.post.PostWithIdAndAuthorVo;
import java.time.LocalDateTime;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
Expand Down Expand Up @@ -41,6 +42,10 @@ public interface PostRepository
List<PostJpaEntity> findTopNPostsByViewCountAndLikeCount(
@Param("since") LocalDateTime since, @Param("limit") int limit);

@Query(
"select new com.ftm.server.application.vo.post.PostWithIdAndAuthorVo(p.id) from PostJpaEntity p order by p.likeCount DESC")
List<PostWithIdAndAuthorVo> findTopNPostsByLikeCount(@Param("limit") int limit);

@Query(
"""
SELECT new com.ftm.server.application.vo.post.PostAndBookmarkCountVo(p.id, COUNT(b))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.ftm.server.adapter.out.persistence.repository;

import com.ftm.server.application.query.FindByIdsQuery;
import com.ftm.server.application.query.FindPostsByCreatedDateQuery;
import com.ftm.server.application.vo.post.PostWithBookmarkCountVo;
import com.ftm.server.application.vo.post.PostWithUserAndBookmarkCountVo;
import com.ftm.server.application.vo.post.UserWithPostCountVo;
import java.util.List;

Expand All @@ -11,4 +13,6 @@ public interface PostWithBookmarkCustomRepository {

List<UserWithPostCountVo> findAllPostsWithUserAndBookmarkCount(
FindPostsByCreatedDateQuery query);

List<PostWithUserAndBookmarkCountVo> findAllPostsWithUserAndBookmarkCount(FindByIdsQuery query);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import static com.ftm.server.adapter.out.persistence.model.QBookmarkJpaEntity.bookmarkJpaEntity;
import static com.ftm.server.adapter.out.persistence.model.QPostJpaEntity.postJpaEntity;

import com.ftm.server.application.query.FindByIdsQuery;
import com.ftm.server.application.query.FindPostsByCreatedDateQuery;
import com.ftm.server.application.vo.post.PostWithBookmarkCountVo;
import com.ftm.server.application.vo.post.PostWithUserAndBookmarkCountVo;
import com.ftm.server.application.vo.post.UserWithPostCountVo;
import com.ftm.server.domain.enums.UserRole;
import com.querydsl.core.types.Projections;
Expand Down Expand Up @@ -81,4 +83,36 @@ public List<UserWithPostCountVo> findAllPostsWithUserAndBookmarkCount(
.groupBy(postJpaEntity.user.id, postJpaEntity.user.nickname)
.fetch();
}

@Override
public List<PostWithUserAndBookmarkCountVo> findAllPostsWithUserAndBookmarkCount(
FindByIdsQuery query) {
return queryFactory
.select(
Projections.constructor(
PostWithUserAndBookmarkCountVo.class,
postJpaEntity.id,
postJpaEntity.user.id,
postJpaEntity.user.nickname,
postJpaEntity.title,
postJpaEntity.content,
postJpaEntity.hashtags,
postJpaEntity.viewCount, // sum() 쓰지 마세요
postJpaEntity.likeCount, // sum() 쓰지 마세요
bookmarkJpaEntity.id.countDistinct() // 북마크 개수
))
.from(postJpaEntity)
.leftJoin(bookmarkJpaEntity)
.on(bookmarkJpaEntity.post.eq(postJpaEntity))
.groupBy(
postJpaEntity.id,
postJpaEntity.user.id,
postJpaEntity.user.nickname,
postJpaEntity.title,
postJpaEntity.content,
postJpaEntity.hashtags,
postJpaEntity.viewCount,
postJpaEntity.likeCount)
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.ftm.server.adapter.out.scheduler;

import com.ftm.server.application.port.out.cache.LoadUserPickBiblePostsWithCachePort;
import com.ftm.server.common.annotation.Adapter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;

@Slf4j
@Adapter
@RequiredArgsConstructor
public class GetUserPickBiblePostsScheduler {

private final LoadUserPickBiblePostsWithCachePort loadUserPickBiblePostsWithCachePort;

// 마지막 실행으로부터 57분 뒤에 재실행됨.
// cache TTL 값인 1시간이 끝나기 이전에 캐시를 업데이트 해 놓는다.
@Scheduled(fixedRateString = "PT57M", initialDelayString = "PT1M")
public void run() {
log.info(
"Loading UserPick Bible Posts at {}",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
loadUserPickBiblePostsWithCachePort.getUserPickBiblePostCachePut();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.ftm.server.application.port.in.user;
package com.ftm.server.application.port.in.post;

import com.ftm.server.application.vo.post.UserPickPopularPostsVo;
import com.ftm.server.common.annotation.UseCase;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ftm.server.application.port.in.post;

import com.ftm.server.application.vo.post.UserPickBiblePostsVo;
import com.ftm.server.common.annotation.UseCase;
import java.util.List;

@UseCase
public interface LoadUserPickBiblePostsUseCase {
List<UserPickBiblePostsVo> execute();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ftm.server.application.port.out.cache;

import com.ftm.server.application.vo.post.PostWithIdAndAuthorVo;
import java.util.List;

public interface LoadUserPickBiblePostsWithCachePort {

List<PostWithIdAndAuthorVo> getUserPickBiblePost();

List<PostWithIdAndAuthorVo> getUserPickBiblePostCachePut();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.ftm.server.application.port.out.persistence.post;

import com.ftm.server.application.query.FindByIdQuery;
import com.ftm.server.application.query.FindByUserIdsQuery;
import com.ftm.server.application.query.FindPostByDeleteOptionQuery;
import com.ftm.server.application.query.FindUserPickPopularPostsQuery;
import com.ftm.server.application.query.*;
import com.ftm.server.application.vo.post.PostWithIdAndAuthorVo;
import com.ftm.server.common.annotation.Port;
import com.ftm.server.domain.entity.Post;
import java.util.List;
Expand All @@ -19,4 +17,6 @@ public interface LoadPostPort {
List<Post> loadPostsByDeleteOption(FindPostByDeleteOptionQuery query);

List<Post> loadUserPickPopularPosts(FindUserPickPopularPostsQuery query);

List<PostWithIdAndAuthorVo> loadUserPickBiblePosts(FindUserPickBiblePostsQuery query);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.ftm.server.application.port.out.persistence.post;

import com.ftm.server.application.query.FindBookmarkCountByPostIdsQuery;
import com.ftm.server.application.query.FindByIdsQuery;
import com.ftm.server.application.query.FindPostsByCreatedDateQuery;
import com.ftm.server.application.vo.post.PostAndBookmarkCountVo;
import com.ftm.server.application.vo.post.PostWithBookmarkCountVo;
import com.ftm.server.application.vo.post.PostWithUserAndBookmarkCountVo;
import com.ftm.server.application.vo.post.UserWithPostCountVo;
import com.ftm.server.common.annotation.Port;
import java.util.List;
Expand All @@ -16,4 +18,6 @@ List<UserWithPostCountVo> loadAllPostsWithUserAndBookmarkCount(
FindPostsByCreatedDateQuery query);

List<PostAndBookmarkCountVo> getPostAndBookmarkCount(FindBookmarkCountByPostIdsQuery query);

List<PostWithUserAndBookmarkCountVo> loadPostWithUserAndBookmarkCount(FindByIdsQuery query);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ftm.server.application.query;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class FindUserPickBiblePostsQuery {

private final Integer limit;

public static FindUserPickBiblePostsQuery of(Integer limit) {
return new FindUserPickBiblePostsQuery(limit);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.ftm.server.application.service.post;

import com.ftm.server.application.port.in.user.GetUserPickPopularPostsUseCase;
import com.ftm.server.application.port.in.post.GetUserPickPopularPostsUseCase;
import com.ftm.server.application.port.out.cache.LoadUserPickPopularWithCachePort;
import com.ftm.server.application.port.out.persistence.post.LoadPostImagePort;
import com.ftm.server.application.port.out.persistence.post.LoadPostWithBookmarkCountPort;
Expand Down
Loading
Loading