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
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseLike {
public abstract class BaseLike {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,14 @@
package com.jiwon.mylog.domain.post.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.jiwon.mylog.domain.post.entity.Post;
import com.jiwon.mylog.domain.post.entity.PostStatus;
import com.jiwon.mylog.domain.user.dto.response.UserResponse;
import com.jiwon.mylog.domain.user.dto.response.UserSummaryResponse;
import com.jiwon.mylog.global.common.enums.Visibility;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@Builder
@AllArgsConstructor
public class MainPostResponse {
private final Long postId;
private final String title;
private final String contentPreview;
private final PostStatus postStatus;
private final Visibility visibility;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private final LocalDateTime createdAt;
private final UserResponse user;

public static MainPostResponse fromPost(Post post) {
return MainPostResponse.builder()
.postId(post.getId())
.title(post.getTitle())
.contentPreview(post.getContentPreview())
.postStatus(post.getPostStatus())
.visibility(post.getVisibility())
.createdAt(post.getCreatedAt())
.user(UserResponse.fromUser(post.getUser()))
.build();
}
public record MainPostResponse(
Long postId, String title, String contentPreview, PostStatus postStatus, Visibility visibility,
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime createdAt,
UserSummaryResponse user) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,6 @@ public interface PostRepository extends JpaRepository<Post, Long>, PostRepositor
countQuery = "select count(p) from Post p where p.deletedAt is null and p.type = 'NOTICE'")
Page<Post> findAllNotice(Pageable pageable);

@Query(value = "select p from Post p " +
"join fetch p.user u " +
"left join fetch u.profileImage " +
"where p.deletedAt is null and p.visibility = 'PUBLIC' and p.type = 'NORMAL' " +
"order by p.createdAt desc",
countQuery = "select count(p) from Post p where p.deletedAt is null and p.visibility = 'PUBLIC' and p.type = 'NORMAL'")
Page<Post> findAll(Pageable pageable);

@Query("select p from Post p join fetch p.user left join fetch p.category where p.id = :id")
Optional<Post> findWithUserAndCategory(@Param("id") Long id);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jiwon.mylog.domain.post.repository;

import com.jiwon.mylog.domain.post.dto.response.MainPostResponse;
import com.jiwon.mylog.domain.post.dto.response.PostDetailResponse;
import com.jiwon.mylog.domain.post.dto.response.PostNavigationResponse;
import com.jiwon.mylog.domain.post.dto.response.PostSummaryResponse;
Expand All @@ -17,6 +18,8 @@ public interface PostRepositoryCustom {

Page<PostSummaryResponse> findFilteredPosts(Long userId, Long categoryId, List<Long> tagIds, String keyword, Pageable pageable);

Page<MainPostResponse> findAllPosts(Pageable pageable);

Optional<PostDetailResponse> findPostDetail(Long postId);

List<UserActivityResponse> findUserActivities(Long userId, LocalDate start, LocalDate end);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import com.jiwon.mylog.domain.comment.entity.QComment;
import com.jiwon.mylog.domain.image.entity.QProfileImage;
import com.jiwon.mylog.domain.like.entity.QPostLike;
import com.jiwon.mylog.domain.post.dto.response.MainPostResponse;
import com.jiwon.mylog.domain.post.dto.response.PostDetailResponse;
import com.jiwon.mylog.domain.post.dto.response.PostNavigationResponse;
import com.jiwon.mylog.domain.post.dto.response.PostSummaryResponse;
import com.jiwon.mylog.domain.post.dto.response.RelatedPostResponse;
import com.jiwon.mylog.domain.post.entity.PostType;
import com.jiwon.mylog.domain.post.entity.QPost;
import com.jiwon.mylog.domain.tag.dto.response.TagResponse;
import com.jiwon.mylog.domain.tag.entity.QPostTag;
Expand All @@ -18,6 +20,7 @@
import com.jiwon.mylog.domain.user.dto.response.UserResponse;
import com.jiwon.mylog.domain.user.dto.response.UserSummaryResponse;
import com.jiwon.mylog.domain.user.entity.QUser;
import com.jiwon.mylog.global.common.enums.Visibility;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.Projections;
Expand Down Expand Up @@ -122,6 +125,42 @@ public Page<PostSummaryResponse> findFilteredPosts(
return new PageImpl<>(posts, pageable, total);
}

@Override
public Page<MainPostResponse> findAllPosts(Pageable pageable) {
BooleanBuilder conditions = new BooleanBuilder()
.and(postDeletedAtIsNull())
.and(postVisibilityEq(Visibility.PUBLIC))
.and(postTypeEq(PostType.NORMAL));

List<MainPostResponse> posts = jpaQueryFactory
.select(Projections.constructor(MainPostResponse.class,
POST.id,
POST.title,
POST.contentPreview,
POST.postStatus,
POST.visibility,
POST.createdAt,
Projections.constructor(UserSummaryResponse.class,
USER.id,
USER.username,
PROFILE_IMAGE.fileKey.coalesce("")
)
)
)
.from(POST)
.join(POST.user, USER)
.leftJoin(USER.profileImage, PROFILE_IMAGE)
.where(conditions)
.orderBy(POST.createdAt.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

Long count = createCountQuery(conditions);

return new PageImpl<>(posts, pageable, count);
}

private List<PostSummaryResponse> createPostSummaryQuery(BooleanBuilder builder, Pageable pageable) {
return jpaQueryFactory
.select(Projections.constructor(PostSummaryResponse.class,
Expand Down Expand Up @@ -478,6 +517,14 @@ private BooleanExpression postDeletedAtIsNull() {
return POST.deletedAt.isNull();
}

private BooleanExpression postVisibilityEq(Visibility visibility) {
return POST.visibility.eq(visibility);
}

private BooleanExpression postTypeEq(PostType type) {
return POST.type.eq(type);
}

private BooleanExpression titleContainsKeyword(String keyword) {
return keyword != null ? POST.title.containsIgnoreCase(keyword) : null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,10 @@ public PostDetailResponse getPost(Long postId) {
condition = "#pageable != null")
@Transactional(readOnly = true)
public PageResponse getPosts(Pageable pageable) {
Page<Post> postPage = postRepository.findAll(pageable);
List<MainPostResponse> posts = postPage.stream()
.map(MainPostResponse::fromPost)
.toList();
Page<MainPostResponse> postPage = postRepository.findAllPosts(pageable);

return PageResponse.from(
posts,
postPage.getContent(),
postPage.getNumber(),
postPage.getSize(),
postPage.getTotalPages(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,11 @@ public class UserStatsController {
private final UserStatsService userStatsService;

@GetMapping("/users/rankers/weekly")
public ResponseEntity<List<UserRankResponse>> getRanker(
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate
) {
LocalDate end = endDate != null ? endDate : LocalDate.now().minusDays(1);
LocalDate start = startDate != null ? startDate : end.minusDays(6);

List<UserRankResponse> response = userStatsService.getRanker(start, end);
public ResponseEntity<List<UserRankResponse>> getRanker() {
List<UserRankResponse> response = userStatsService.getRanker();
return ResponseEntity.ok(response);
}


@GetMapping("/users/stats")
public ResponseEntity<DailyReportResponse> getDailyStats(
@LoginUser Long userId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

import com.jiwon.mylog.domain.statistic.dto.DailyReportResponse;
import com.jiwon.mylog.domain.statistic.dto.UserRankResponse;
import com.jiwon.mylog.domain.statistic.entity.UserDailyStats;
import com.jiwon.mylog.domain.statistic.entity.UserWeeklyRanker;
import com.jiwon.mylog.domain.statistic.repository.UserStatsRepository;
import com.jiwon.mylog.domain.statistic.repository.UserWeeklyRankerRepository;
import com.jiwon.mylog.global.redis.key.RedisKey;
import com.jiwon.mylog.global.redis.RedisUtil;
import java.util.Collections;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
Expand All @@ -20,14 +25,26 @@ public class UserStatsService {
private final RedisUtil redisUtil;

private final UserStatsRepository userStatsRepository;
private final UserWeeklyRankerRepository userWeeklyRankerRepository;

private final static String REDIS_SET_DEFAULT = "0";
private final static int REDIS_GET_DEFAULT = 0;

@Cacheable(value = "stat::ranker", key = "'start:' + #start + ':end:' + #end")
@Cacheable(value = "stat::ranker")
@Transactional(readOnly = true)
public List<UserRankResponse> getRanker(LocalDate start, LocalDate end) {
return userStatsRepository.findWeeklyTopUsers(start, end, 3);
public List<UserRankResponse> getRanker() {
Optional<LocalDate> latest = userWeeklyRankerRepository.findLatestWeekStartDate();

if (latest.isEmpty()) {
return Collections.emptyList();
}

List<UserWeeklyRanker> rankers =
userWeeklyRankerRepository.findAllByWeekStart(latest.get());

return rankers.stream()
.map(UserRankResponse::fromRanker)
.toList();
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.jiwon.mylog.domain.statistic.dto;

import com.jiwon.mylog.domain.user.dto.response.UserSummaryResponse;
import com.jiwon.mylog.domain.statistic.entity.UserWeeklyRanker;
import lombok.Builder;

@Builder
public record UserRankResponse(
Long userId,
String username,
Expand All @@ -10,5 +12,20 @@ public record UserRankResponse(
int receivedComments,
int createdPosts,
int createdComments,
int total) {
long total) {

public static UserRankResponse fromRanker(UserWeeklyRanker ranker) {
return UserRankResponse.builder()
.userId(ranker.getUser().getId())
.username(ranker.getUser().getUsername())
.imageKey(ranker.getUser().getProfileImage() != null ?
ranker.getUser().getProfileImage().getFileKey() :
"")
.receivedLikes(ranker.getReceivedLikes())
.receivedComments(ranker.getReceivedComments())
.createdComments(ranker.getCreatedComments())
.createdPosts(ranker.getCreatedPosts())
.total(ranker.getTotalScore())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.jiwon.mylog.domain.statistic.entity;

import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.ColumnDefault;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@SuperBuilder
@MappedSuperclass
public abstract class BaseStats {

@Column(nullable = false)
@ColumnDefault("0")
private int receivedLikes = 0;

@Column(nullable = false)
@ColumnDefault("0")
private int receivedComments = 0;

@Column(nullable = false)
@ColumnDefault("0")
private int createdPosts = 0;

@Column(nullable = false)
@ColumnDefault("0")
private int createdComments = 0;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.jiwon.mylog.domain.statistic;
package com.jiwon.mylog.domain.statistic.entity;

import com.jiwon.mylog.domain.user.entity.User;
import jakarta.persistence.Entity;
Expand All @@ -10,16 +10,15 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import lombok.experimental.SuperBuilder;

@NoArgsConstructor
@AllArgsConstructor
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SuperBuilder
@Getter
@Entity
@Table(
Expand All @@ -28,7 +27,7 @@
columnNames = {"user_id", "date"}
)
)
public class UserDailyStats {
public class UserDailyStats extends BaseStats {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand All @@ -39,14 +38,6 @@ public class UserDailyStats {

private LocalDate date;

private int receivedLikes = 0;

private int receivedComments = 0;

private int createdPosts = 0;

private int createdComments = 0;

public static UserDailyStats empty(LocalDate date) {
return UserDailyStats.builder()
.date(date)
Expand Down
Loading