Skip to content

Commit 27a606c

Browse files
authored
Merge pull request #354 from MT-TEAM-Org/epic/hotBoardAndLatestComment
epic: latest comment get add and hot board list fix
2 parents d4cef2f + 046ccce commit 27a606c

9 files changed

Lines changed: 263 additions & 0 deletions

File tree

src/main/java/org/myteam/server/board/repository/BoardQueryRepository.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.myteam.server.global.util.redis.CommonCountDto;
4141
import org.myteam.server.global.util.redis.ServiceType;
4242
import org.myteam.server.global.util.redis.service.RedisCountService;
43+
import org.myteam.server.global.util.redis.service.RedisService;
4344
import org.myteam.server.home.dto.HotBoardDto;
4445
import org.myteam.server.home.dto.NewBoardDto;
4546
import org.myteam.server.report.domain.DomainType;
@@ -57,6 +58,7 @@ public class BoardQueryRepository {
5758
private final JPAQueryFactory queryFactory;
5859
private final RedisBoardRankingReader rankingReader;
5960
private final RedisCountService redisCountService;
61+
private final RedisService redisService;
6062

6163
/**
6264
* 게시글 목록 조회
@@ -488,6 +490,33 @@ public List<HotBoardDto> getHotBoardList() {
488490

489491
return hotBoardList;
490492
}
493+
public List<HotBoardDto> zSetGetHotBoardList(){
494+
List<Long> boardId=redisService.getBoardRecommendRankPerDay();
495+
List<HotBoardDto> hotBoardList = queryFactory
496+
.select(Projections.fields(HotBoardDto.class,
497+
board.boardType,
498+
board.categoryType,
499+
board.id,
500+
board.title,
501+
board.id.in(boardId).as("isHot"),
502+
board.id.in(getNewBoardIdList()).as("isNew"),
503+
new CaseBuilder()
504+
.when(board.thumbnail.isNotEmpty()).then(true)
505+
.otherwise(false)
506+
.as("isImage")
507+
))
508+
.from(board)
509+
.where(board.id.in(boardId))
510+
.fetch();
511+
Map<Long, Integer> orderMap = IntStream.range(0, boardId.size())
512+
.boxed()
513+
.collect(Collectors.toMap(boardId::get, i -> i));
514+
hotBoardList.sort(Comparator.comparingInt(dto -> orderMap.getOrDefault(dto.getId(), Integer.MAX_VALUE)));
515+
// 순위 부여
516+
AtomicInteger rankCounter = new AtomicInteger(1);
517+
hotBoardList.forEach(dto -> dto.setRank(rankCounter.getAndIncrement()));
518+
return hotBoardList;
519+
}
491520

492521
public Long findPreviousBoardId(Long boardId, Category boardType, CategoryType categoryType) {
493522
return queryFactory

src/main/java/org/myteam/server/comment/controller/CommentController.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.myteam.server.comment.controller;
22

3+
import static org.myteam.server.comment.dto.response.CommentResponse.*;
34
import static org.myteam.server.global.web.response.ResponseStatus.SUCCESS;
45

56
import io.swagger.v3.oas.annotations.Operation;
@@ -14,6 +15,7 @@
1415
import org.myteam.server.comment.dto.request.CommentRequest.CommentDeleteRequest;
1516
import org.myteam.server.comment.dto.request.CommentRequest.CommentListRequest;
1617
import org.myteam.server.comment.dto.request.CommentRequest.CommentSaveRequest;
18+
import org.myteam.server.comment.dto.response.CommentResponse;
1719
import org.myteam.server.comment.dto.response.CommentResponse.BestCommentSaveListResponse;
1820
import org.myteam.server.comment.dto.response.CommentResponse.CommentSaveListResponse;
1921
import org.myteam.server.comment.dto.response.CommentResponse.CommentSaveResponse;
@@ -33,6 +35,8 @@
3335
import org.springframework.web.bind.annotation.RequestMapping;
3436
import org.springframework.web.bind.annotation.RestController;
3537

38+
import java.util.List;
39+
3640
@RestController
3741
@RequestMapping("/api/comments")
3842
@RequiredArgsConstructor
@@ -169,4 +173,19 @@ public ResponseEntity<ResponseDto<BestCommentSaveListResponse>> getBestComments(
169173
bestComments
170174
));
171175
}
176+
@Operation(summary = "최신 댓글 조회", description = "최신 댓글들을 조회합니다.")
177+
@ApiResponses(value = {
178+
@ApiResponse(responseCode = "200", description = "최신 댓글 목록 조회 성공"),
179+
@ApiResponse(responseCode = "404", description = "게시글이 존재하지 않음", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
180+
})
181+
@GetMapping("/latest")
182+
public ResponseEntity<ResponseDto<List<LatestCommentListResponse>>> getLatestComments() {
183+
List<LatestCommentListResponse>
184+
latestCommentListResponse=commentReadService.getLatestComments();
185+
return ResponseEntity.ok(new ResponseDto<>(
186+
SUCCESS.name(),
187+
"베스트 댓글 목록 조회 성공",
188+
latestCommentListResponse
189+
));
190+
}
172191
}

src/main/java/org/myteam/server/comment/dto/response/CommentResponse.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
import java.time.LocalDateTime;
44
import java.util.List;
55
import java.util.UUID;
6+
7+
import io.swagger.v3.oas.annotations.media.Schema;
68
import lombok.AllArgsConstructor;
79
import lombok.Builder;
810
import lombok.Data;
911
import lombok.Getter;
1012
import lombok.NoArgsConstructor;
1113
import lombok.Setter;
1214
import org.myteam.server.comment.domain.Comment;
15+
import org.myteam.server.comment.domain.CommentType;
1316
import org.myteam.server.global.page.response.PageCustomResponse;
1417
import org.myteam.server.util.ClientUtils;
1518

@@ -122,4 +125,12 @@ public static BestCommentSaveListResponse createResponse(
122125
.build();
123126
}
124127
}
128+
129+
@AllArgsConstructor
130+
public static class LatestCommentListResponse{
131+
private Long commentId;
132+
@Schema(description = "최신댓글이 어떤 게시글에 대한 댓글인지를 보여줍니다.")
133+
private CommentType commentType;
134+
private String content;
135+
}
125136
}

src/main/java/org/myteam/server/comment/repository/CommentQueryRepository.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static org.myteam.server.comment.domain.QInquiryComment.inquiryComment;
1010
import static org.myteam.server.comment.domain.QNewsComment.newsComment;
1111
import static org.myteam.server.comment.domain.QNoticeComment.noticeComment;
12+
import static org.myteam.server.comment.dto.response.CommentResponse.*;
1213
import static org.myteam.server.improvement.domain.QImprovement.improvement;
1314
import static org.myteam.server.inquiry.domain.QInquiry.inquiry;
1415
import static org.myteam.server.member.entity.QMember.member;
@@ -45,6 +46,7 @@
4546
import org.myteam.server.comment.domain.QMatchComment;
4647
import org.myteam.server.comment.domain.QNewsComment;
4748
import org.myteam.server.comment.domain.QNoticeComment;
49+
import org.myteam.server.comment.dto.response.CommentResponse;
4850
import org.myteam.server.comment.dto.response.CommentResponse.BestCommentResponse;
4951
import org.myteam.server.comment.dto.response.CommentResponse.CommentSaveResponse;
5052
import org.myteam.server.comment.service.CommentRecommendReadService;
@@ -69,6 +71,20 @@ public class CommentQueryRepository {
6971
private final CommentRepository commentRepository;
7072
private final CommentRecommendReadService commentRecommendReadService;
7173

74+
public List<LatestCommentListResponse> getNewestComment(){
75+
List<LatestCommentListResponse> comments=queryFactory
76+
.select(Projections.constructor(LatestCommentListResponse.class,
77+
comment1.id,
78+
comment1.commentType,
79+
comment1.comment.substring(0,20)))
80+
.from(comment1)
81+
.orderBy(comment1.createDate.desc())
82+
.limit(10)
83+
.fetch();
84+
85+
return comments;
86+
}
87+
7288
/**
7389
* 대댓글 목록 조회
7490
*/

src/main/java/org/myteam/server/comment/service/CommentReadService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package org.myteam.server.comment.service;
22

3+
import java.util.List;
34
import java.util.UUID;
45
import lombok.RequiredArgsConstructor;
56
import lombok.extern.slf4j.Slf4j;
67
import org.myteam.server.comment.domain.Comment;
78
import org.myteam.server.comment.domain.CommentType;
89
import org.myteam.server.comment.dto.request.CommentRequest.CommentListRequest;
10+
import org.myteam.server.comment.dto.response.CommentResponse;
911
import org.myteam.server.comment.dto.response.CommentResponse.BestCommentResponse;
1012
import org.myteam.server.comment.dto.response.CommentResponse.BestCommentSaveListResponse;
1113
import org.myteam.server.comment.dto.response.CommentResponse.CommentSaveListResponse;
@@ -25,6 +27,8 @@
2527
import org.springframework.stereotype.Service;
2628
import org.springframework.transaction.annotation.Transactional;
2729

30+
import static org.myteam.server.comment.dto.response.CommentResponse.*;
31+
2832
@Slf4j
2933
@Service
3034
@RequiredArgsConstructor
@@ -36,6 +40,9 @@ public class CommentReadService {
3640
private final SecurityReadService securityReadService;
3741
private final CommentRecommendReadService commentRecommendReadService;
3842

43+
public List<LatestCommentListResponse> getLatestComments(){
44+
return commentQueryRepository.getNewestComment();
45+
}
3946
public Comment findById(Long commentId) {
4047
return commentRepository.findById(commentId)
4148
.orElseThrow(() -> new PlayHiveException(ErrorCode.COMMENT_NOT_FOUND));

src/main/java/org/myteam/server/global/util/redis/service/RedisService.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package org.myteam.server.global.util.redis.service;
22

3+
import java.sql.Date;
34
import java.time.Duration;
5+
import java.time.LocalDateTime;
6+
import java.time.LocalTime;
7+
import java.time.ZoneId;
8+
import java.util.Collections;
9+
import java.util.List;
410
import java.util.UUID;
11+
import java.util.stream.Collectors;
512

613
import org.myteam.server.admin.utill.StaticDataType;
714
import org.springframework.data.redis.core.RedisTemplate;
15+
import org.springframework.data.redis.core.script.DefaultRedisScript;
816
import org.springframework.stereotype.Service;
917

1018
import lombok.RequiredArgsConstructor;
@@ -16,6 +24,7 @@
1624
public class RedisService { // TODO: RedisReportService 로 변경.
1725

1826
private final RedisTemplate<String, String> redisTemplate;
27+
private static final String BOARD_RANK_KEY=":BoardRank";
1928
private static final String ADMIN_ALARM_KEY="ADMIN_ALARM";
2029
private static final int ADMIN_LOGIN_MAX_REQUESTS=10;
2130
private static final int MAX_REQUESTS = 3; // 제한 횟수 (기본값: 5분 동안 3회)
@@ -91,6 +100,32 @@ public void adminReadCheckUpdate(String adminIdentifier, StaticDataType staticDa
91100
redisTemplate.expire(redisKey, Duration.ofDays(30L));
92101
}
93102
}
103+
104+
public void boardRecommendRankPerDay(Long contentId,Long delta){
105+
LocalDateTime now = LocalDateTime.now().with(LocalTime.MIDNIGHT);
106+
String key = now+BOARD_RANK_KEY;
107+
LocalDateTime nextMidnight = now.plusDays(1).with(LocalTime.MIDNIGHT);
108+
redisTemplate.opsForZSet().incrementScore(key,contentId.toString(),delta);
109+
redisTemplate.expireAt(key,Date.valueOf(nextMidnight.toLocalDate()));
110+
}
111+
public List<Long> getBoardRecommendRankPerDay(){
112+
LocalDateTime now=LocalDateTime.now().with(LocalTime.MIDNIGHT);
113+
String key=now+BOARD_RANK_KEY;
114+
List<Long> ids=redisTemplate.opsForZSet().reverseRangeWithScores(key,0,9)
115+
.stream()
116+
.filter(x->{
117+
if(x.getScore()>0){
118+
return true;
119+
}
120+
return false;
121+
})
122+
.map(x->{
123+
return Long.parseLong(x.getValue());
124+
})
125+
.collect(Collectors.toList());
126+
return ids;
127+
}
128+
94129
/**
95130
* 요청 제한을 적용할 Redis Key 생성
96131
*

src/main/java/org/myteam/server/recommend/BoardRecommendHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.myteam.server.board.service.BoardRecommendReadService;
1010
import org.myteam.server.global.exception.ErrorCode;
1111
import org.myteam.server.global.exception.PlayHiveException;
12+
import org.myteam.server.global.util.redis.service.RedisService;
1213
import org.myteam.server.member.entity.Member;
1314
import org.myteam.server.report.domain.DomainType;
1415
import org.springframework.stereotype.Component;
@@ -20,6 +21,7 @@ public class BoardRecommendHandler implements RecommendHandler {
2021
private final BoardRecommendReadService boardRecommendReadService;
2122
private final BoardRecommendRepository boardRecommendRepository;
2223
private final BoardRepository boardRepository;
24+
private final RedisService redisService;
2325

2426
@Override
2527
public boolean supports(DomainType type) {
@@ -39,10 +41,12 @@ public void saveRecommendation(Long contentId, Member member) {
3941
.orElseThrow(() -> new PlayHiveException(ErrorCode.BOARD_NOT_FOUND));
4042
BoardRecommend recommend = BoardRecommend.builder().board(board).member(member).build();
4143
boardRecommendRepository.save(recommend);
44+
redisService.boardRecommendRankPerDay(contentId,1L);
4245
}
4346

4447
@Override
4548
public void deleteRecommendation(Long contentId, UUID userId) {
4649
boardRecommendRepository.deleteByBoardIdAndMemberPublicId(contentId, userId);
50+
redisService.boardRecommendRankPerDay(contentId,-1L);
4751
}
4852
}

0 commit comments

Comments
 (0)