2121import com .chungang .capstone .openstep .global .apiPayload .exception .handler .IssueHandler ;
2222import lombok .RequiredArgsConstructor ;
2323import lombok .extern .slf4j .Slf4j ;
24+ import org .springframework .data .domain .Pageable ;
2425import org .springframework .stereotype .Service ;
2526
2627import java .time .LocalDateTime ;
@@ -43,9 +44,51 @@ public class IssueQueryService {
4344 private final MemberDomainRepository memberDomainRepository ;
4445 private final BookmarkRepository bookmarkRepository ;
4546
46- public List <Issue > getTrendingIssues () {
47- List <Repo > repos = repoRepository .findAll ();
47+ // public List<Issue> getTrendingIssues(Pageable pageable) {
48+ // List<Repo> repos = repoRepository.findAll();
49+ // List<Issue> allIssues = new ArrayList<>();
50+ // for (Repo repo : repoRepository.findTop10ByOrderByStarsDesc()) {
51+ // GitHubIssueResponse res = gitHubGraphQLService.fetchIssuesByRepo(repo.getOwnerName(), repo.getRepoName());
52+ // if (res != null && res.getData().getRepository() != null) {
53+ // List<Issue> issues = res.getData().getRepository().getIssues().getNodes().stream()
54+ // .map(node -> {
55+ // Optional<Issue> existing = issueRepository.findByGithubUrl(node.getUrl());
56+ // if (existing.isPresent()) {
57+ // Issue issue = existing.get();
58+ // LocalDateTime updated = OffsetDateTime.parse(node.getUpdatedAt()).toLocalDateTime();
59+ // if (issue.getUpdatedAt().isEqual(updated)) return issue; // skip
60+ // }
61+ // return saveIfNotExistsOrUpdate(node, repo);
62+ // })
63+ // .filter(Objects::nonNull)
64+ // .toList();
65+ // allIssues.addAll(issues);
66+ // }
67+ // if (allIssues.size() >= 20) break;
68+ // }
69+ // List<Issue> top20 = allIssues.stream()
70+ // .sorted(Comparator.comparing(Issue::getUpdatedAt).reversed())
71+ // .limit(20)
72+ // .toList();
73+ //
74+ // int start = (int) pageable.getOffset();
75+ // int end = Math.min(start + pageable.getPageSize(), top20.size());
76+ // if (start >= end) return List.of();
77+ // return top20.subList(start, end);
78+ // }
79+
80+ public List <Issue > getTrendingIssues (Pageable pageable ) {
81+ List <Issue > cached = issueCacheService .getTrendingIssuesFromCache ();
82+ if (cached != null && !cached .isEmpty ()) {
83+ log .info ("[TRENDING] Cache hit: {} issues" , cached .size ());
84+ int start = (int ) pageable .getOffset ();
85+ int end = Math .min (start + pageable .getPageSize (), cached .size ());
86+ return (start >= end ) ? List .of () : cached .subList (start , end );
87+ }
88+
89+ log .info ("[TRENDING] Cache miss: fetching from GitHub..." );
4890 List <Issue > allIssues = new ArrayList <>();
91+
4992 for (Repo repo : repoRepository .findTop10ByOrderByStarsDesc ()) {
5093 GitHubIssueResponse res = gitHubGraphQLService .fetchIssuesByRepo (repo .getOwnerName (), repo .getRepoName ());
5194 if (res != null && res .getData ().getRepository () != null ) {
@@ -55,7 +98,7 @@ public List<Issue> getTrendingIssues() {
5598 if (existing .isPresent ()) {
5699 Issue issue = existing .get ();
57100 LocalDateTime updated = OffsetDateTime .parse (node .getUpdatedAt ()).toLocalDateTime ();
58- if (issue .getUpdatedAt ().isEqual (updated )) return issue ; // skip
101+ if (issue .getUpdatedAt ().isEqual (updated )) return issue ;
59102 }
60103 return saveIfNotExistsOrUpdate (node , repo );
61104 })
@@ -64,10 +107,22 @@ public List<Issue> getTrendingIssues() {
64107 allIssues .addAll (issues );
65108 }
66109 }
67- return allIssues ;
110+
111+ // 정렬 및 캐시 저장
112+ List <Issue > sorted = allIssues .stream ()
113+ .sorted (Comparator .comparing (Issue ::getUpdatedAt ).reversed ())
114+ .toList ();
115+
116+ issueCacheService .saveTrendingIssues (sorted );
117+
118+ int start = (int ) pageable .getOffset ();
119+ int end = Math .min (start + pageable .getPageSize (), sorted .size ());
120+ return (start >= end ) ? List .of () : sorted .subList (start , end );
68121 }
69122
70- public List <Issue > getSuggestedIssues (Member member ) {
123+
124+
125+ public List <Issue > getSuggestedIssues (Member member , Pageable pageable ) {
71126 Long memberId = member .getMemberId ();
72127 log .info ("[ISSUE_RECOMMEND] Start for memberId = {}" , memberId );
73128
@@ -78,7 +133,10 @@ public List<Issue> getSuggestedIssues(Member member) {
78133 List <Issue > cached = issueCacheService .getRecommendedIssues (memberId );
79134 if (cached != null && currentHash .equals (cachedHash )) {
80135 log .info ("[ISSUE_RECOMMEND] Cache hit: {} issues (interests same)" , cached .size ());
81- return cached ;
136+ int start = (int ) pageable .getOffset ();
137+ int end = Math .min (start + pageable .getPageSize (), cached .size ());
138+ if (start >= end ) return List .of ();
139+ return cached .subList (start , end );
82140 }
83141
84142 // 관심사 바뀐 경우 캐시 무효화
@@ -205,7 +263,10 @@ public List<Issue> getSuggestedIssues(Member member) {
205263 if (summarized .size () < 20 ) {
206264 log .warn ("[ISSUE_RECOMMEND] WARNING: Recommended issue count below target ({} / 20)" , summarized .size ());
207265 }
208- return summarized ;
266+ int start = (int ) pageable .getOffset ();
267+ int end = Math .min (start + pageable .getPageSize (), summarized .size ());
268+ if (start >= end ) return List .of ();
269+ return summarized .subList (start , end );
209270 }
210271
211272
@@ -307,7 +368,12 @@ public List<Long> getBookmarkedIssueIds(Long memberId) {
307368 .collect (Collectors .toList ());
308369 }
309370
310- public List <Issue > searchGitHubIssuesByKeywordAndFilters (String keyword , List <InterestLanguage > languages , UpdatePeriod updatePeriod ) {
371+ public List <Issue > searchGitHubIssuesByKeywordAndFilters (
372+ String keyword ,
373+ List <InterestLanguage > languages ,
374+ UpdatePeriod updatePeriod ,
375+ Pageable pageable
376+ ) {
311377 String languageQuery = (languages != null && !languages .isEmpty ())
312378 ? " language:" + languages .stream ()
313379 .map (InterestLanguage ::getLabel )
@@ -328,7 +394,7 @@ public List<Issue> searchGitHubIssuesByKeywordAndFilters(String keyword, List<In
328394 .map (GitHubIssueResponse .Search ::getEdges )
329395 .orElse (Collections .emptyList ());
330396
331- return edges .stream ()
397+ List < Issue > filtered = edges .stream ()
332398 .map (GitHubIssueResponse .Edge ::getNode )
333399 .filter (Objects ::nonNull )
334400 .filter (node -> node .getUpdatedAt () != null )
@@ -337,14 +403,21 @@ public List<Issue> searchGitHubIssuesByKeywordAndFilters(String keyword, List<In
337403 OffsetDateTime updatedAt = OffsetDateTime .parse (node .getUpdatedAt ());
338404 return updatedAt .isAfter (threshold ) ? node : null ;
339405 } catch (Exception e ) {
340- return null ; // 날짜 파싱 실패 시 무시
406+ return null ;
341407 }
342408 })
343409 .filter (Objects ::nonNull )
344- .limit (20 )
345410 .map (IssueConverter ::fromGitHubIssueNode )
411+ .limit (20 )
346412 .toList ();
413+
414+ int start = (int ) pageable .getOffset ();
415+ int end = Math .min (start + pageable .getPageSize (), filtered .size ());
416+ if (start >= end ) return List .of ();
417+
418+ return filtered .subList (start , end );
347419 }
348420
349421
422+
350423}
0 commit comments