Skip to content

Commit fc552d5

Browse files
authored
Merge pull request #136 from Pinback-Team/feat/#127
feat: 아티클 조회 response에 메타데이터 포함
2 parents 6464748 + 206a0a1 commit fc552d5

23 files changed

Lines changed: 765 additions & 2 deletions

File tree

api/src/main/java/com/pinback/api/article/controller/ArticleControllerV3.java

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
package com.pinback.api.article.controller;
22

3+
import java.time.LocalDateTime;
4+
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.PathVariable;
37
import org.springframework.web.bind.annotation.PostMapping;
48
import org.springframework.web.bind.annotation.RequestBody;
59
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RequestParam;
611
import org.springframework.web.bind.annotation.RestController;
712

813
import com.pinback.api.article.dto.request.ArticleCreateRequest;
14+
import com.pinback.application.article.dto.query.PageQuery;
15+
import com.pinback.application.article.dto.response.ArticleCountInfoResponse;
16+
import com.pinback.application.article.dto.response.ArticleDetailResponseV3;
17+
import com.pinback.application.article.dto.response.ArticlesPageResponseV3;
18+
import com.pinback.application.article.dto.response.GetAllArticlesResponseV3;
19+
import com.pinback.application.article.dto.response.TodayRemindResponseV3;
920
import com.pinback.application.article.port.in.CreateArticlePort;
21+
import com.pinback.application.article.port.in.GetArticlePort;
1022
import com.pinback.domain.user.entity.User;
1123
import com.pinback.infrastructure.article.service.ArticleUpdateService;
1224
import com.pinback.shared.annotation.CurrentUser;
@@ -26,6 +38,7 @@
2638
@Tag(name = "ArticleV3", description = "아티클 관리 API V3")
2739
public class ArticleControllerV3 {
2840
private final CreateArticlePort createArticlePort;
41+
private final GetArticlePort getArticlePort;
2942
private final ArticleUpdateService articleMetadataUpdateService;
3043

3144
@Operation(summary = "아티클 생성v3", description = "url에서 썸네일과 제목을 추출하여 새로운 아티클을 생성합니다")
@@ -38,6 +51,89 @@ public ResponseDto<Void> createArticle(
3851
return ResponseDto.ok();
3952
}
4053

54+
@Operation(summary = "아티클 상세 조회 V3", description = "아티클의 상세 정보를 조회합니다")
55+
@GetMapping("/{articleId}")
56+
public ResponseDto<ArticleDetailResponseV3> getArticleDetail(
57+
@Parameter(hidden = true) @CurrentUser User user,
58+
@Parameter(description = "아티클 ID") @PathVariable Long articleId
59+
) {
60+
ArticleDetailResponseV3 response = getArticlePort.getArticleDetailWithMetadata(user, articleId);
61+
return ResponseDto.ok(response);
62+
}
63+
64+
@Operation(summary = "리마인드 아티클 조회 v3", description = "오늘 리마인드할 아티클을 읽음/안읽음 상태별로 조회합니다.")
65+
@GetMapping("/remind")
66+
public ResponseDto<TodayRemindResponseV3> getRemindArticlesV3(
67+
@Parameter(hidden = true) @CurrentUser User user,
68+
@Parameter(description = "현재 시간", example = "2026-02-13T10:00:00") @RequestParam LocalDateTime now,
69+
@Parameter(description = "읽음 상태 (true: 읽음, false: 안읽음)", example = "true") @RequestParam(name = "read-status") boolean readStatus,
70+
@Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
71+
@Parameter(description = "페이지 크기") @RequestParam(defaultValue = "8") int size
72+
) {
73+
PageQuery query = new PageQuery(page, size);
74+
TodayRemindResponseV3 response = getArticlePort.getRemindArticlesV3(user, now, readStatus, query);
75+
return ResponseDto.ok(response);
76+
}
77+
78+
@Operation(summary = "리마인드 아티클 읽음/안읽음 개수 조회 v3", description = "오늘 리마인드할 아티클의 읽음/안읽음 개수를 반환합니다.")
79+
@GetMapping("/remind/count")
80+
public ResponseDto<ArticleCountInfoResponse> getRemindArticlesInfo(
81+
@Parameter(hidden = true) @CurrentUser User user,
82+
@Parameter(description = "현재 시간", example = "2026-02-13T10:00:00") @RequestParam LocalDateTime now
83+
) {
84+
ArticleCountInfoResponse response = getArticlePort.getRemindArticlesInfo(user, now);
85+
return ResponseDto.ok(response);
86+
}
87+
88+
@Operation(summary = "나의 북마크 전체 아티클 조회 V3", description = "사용자의 모든 아티클을 페이징으로 조회합니다.")
89+
@GetMapping
90+
public ResponseDto<GetAllArticlesResponseV3> getAllArticles(
91+
@Parameter(hidden = true) @CurrentUser User user,
92+
@Parameter(description = "읽음 상태 (생략: 전체보기, false: 안읽음)", example = "false") @RequestParam(name = "read-status", required = false) Boolean readStatus,
93+
@Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
94+
@Parameter(description = "페이지 크기") @RequestParam(defaultValue = "8") int size
95+
) {
96+
PageQuery query = new PageQuery(page, size);
97+
GetAllArticlesResponseV3 response = getArticlePort.getAllArticlesV3(user, readStatus, query);
98+
return ResponseDto.ok(response);
99+
}
100+
101+
@Operation(summary = "나의 북마크 아티클 전체보기/안읽음 개수 조회 v3", description = "나의 북마크 아티클의 전체보기/안읽음 개수를 반환합니다.")
102+
@GetMapping("/count")
103+
public ResponseDto<ArticleCountInfoResponse> getArticlesInfo(
104+
@Parameter(hidden = true) @CurrentUser User user
105+
) {
106+
ArticleCountInfoResponse response = getArticlePort.getAllArticlesInfo(user);
107+
return ResponseDto.ok(response);
108+
}
109+
110+
@Operation(summary = "나의 북마크 카테고리별 아티클 조회 V3", description = "특정 카테고리의 아티클 전체 목록을 조회합니다.")
111+
@GetMapping("/category")
112+
public ResponseDto<ArticlesPageResponseV3> getAllArticlesByCategory(
113+
@Parameter(hidden = true) @CurrentUser User user,
114+
@Parameter(description = "카테고리 ID") @RequestParam(name = "category-id") Long categoryId,
115+
@Parameter(description = "읽음 상태 (생략시: 전체보기, false: 안읽음)", example = "false") @RequestParam(name = "read-status", required = false) Boolean readStatus,
116+
@Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
117+
@Parameter(description = "페이지 크기") @RequestParam(defaultValue = "8") int size
118+
) {
119+
PageQuery query = new PageQuery(page, size);
120+
ArticlesPageResponseV3 response = getArticlePort.getAllArticlesByCategoryV3(user, categoryId, readStatus,
121+
query);
122+
return ResponseDto.ok(response);
123+
}
124+
125+
@Operation(summary = "나의 북마크 카테고리별 아티클 전체보기/안읽음 개수 조회 v3", description = "나의 북마크에서 특정 카테고리 아티클의 전체보기/안읽음 개수를 반환합니다.")
126+
@GetMapping("/category/count")
127+
public ResponseDto<ArticleCountInfoResponse> getArticlesInfoByCategory(
128+
@Parameter(hidden = true) @CurrentUser User user,
129+
@Parameter(description = "카테고리 ID") @RequestParam(name = "category-id") Long categoryId
130+
131+
) {
132+
ArticleCountInfoResponse response = getArticlePort.getAllArticlesInfoByCategoryV3(user, categoryId);
133+
return ResponseDto.ok(response);
134+
}
135+
136+
// 기존 아티클 메타데이터 처리 후 삭제 예정
41137
@PostMapping("/metadata")
42138
public ResponseDto<Void> migrateMetadata() {
43139
articleMetadataUpdateService.migrateMissingMetadata();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.pinback.application.article.dto;
2+
3+
public record ArticleCountInfoDtoV3(
4+
long totalCount,
5+
long readCount,
6+
long unreadCount
7+
) {
8+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.pinback.application.article.dto;
2+
3+
import org.springframework.data.domain.Page;
4+
5+
import com.pinback.domain.article.entity.Article;
6+
7+
public record ArticlesWithCountDto(
8+
long totalCount,
9+
long unreadCount,
10+
Page<Article> article
11+
) {
12+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.pinback.application.article.dto.response;
2+
3+
public record ArticleCountInfoResponse(
4+
long totalArticleCount,
5+
long readArticleCount,
6+
long unreadArticleCount
7+
) {
8+
public static ArticleCountInfoResponse of(
9+
long totalArticleCount,
10+
long readArticleCount,
11+
long unreadArticleCount
12+
) {
13+
return new ArticleCountInfoResponse(totalArticleCount, readArticleCount, unreadArticleCount);
14+
}
15+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.pinback.application.article.dto.response;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.pinback.application.category.dto.response.CategoryResponse;
6+
import com.pinback.domain.article.entity.Article;
7+
8+
public record ArticleDetailResponseV3(
9+
long articleId,
10+
String url,
11+
String title,
12+
String thumbnailUrl,
13+
String memo,
14+
LocalDateTime remindAt,
15+
LocalDateTime createdAt,
16+
CategoryResponse categoryResponse
17+
) {
18+
public static ArticleDetailResponseV3 from(Article article) {
19+
return new ArticleDetailResponseV3(
20+
article.getId(),
21+
article.getUrl(),
22+
article.getTitle(),
23+
article.getThumbnail(),
24+
article.getMemo(),
25+
article.getRemindAt(),
26+
article.getCreatedAt(),
27+
CategoryResponse.from(article.getCategory())
28+
);
29+
}
30+
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.pinback.application.article.dto.response;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.pinback.application.category.dto.response.CategoryResponse;
6+
import com.pinback.domain.article.entity.Article;
7+
8+
public record ArticleResponseV3(
9+
long articleId,
10+
String url,
11+
String title,
12+
String thumbnailUrl,
13+
String memo,
14+
LocalDateTime createdAt,
15+
boolean isRead,
16+
CategoryResponse category
17+
) {
18+
public static ArticleResponseV3 from(Article article) {
19+
return new ArticleResponseV3(
20+
article.getId(),
21+
article.getUrl(),
22+
article.getTitle(),
23+
article.getThumbnail(),
24+
article.getMemo(),
25+
article.getCreatedAt(),
26+
article.isRead(),
27+
CategoryResponse.from(article.getCategory())
28+
);
29+
}
30+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.pinback.application.article.dto.response;
2+
3+
import java.util.List;
4+
5+
public record ArticlesPageResponseV3(
6+
long totalArticleCount,
7+
long unreadArticleCount,
8+
String categoryName,
9+
List<CategoryArticleResponseV3> articles
10+
) {
11+
public static ArticlesPageResponseV3 of(long totalArticleCount, long unreadArticleCount, String categoryName,
12+
List<CategoryArticleResponseV3> articles) {
13+
return new ArticlesPageResponseV3(totalArticleCount, unreadArticleCount, categoryName, articles);
14+
}
15+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.pinback.application.article.dto.response;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.pinback.domain.article.entity.Article;
6+
7+
public record CategoryArticleResponseV3(
8+
long articleId,
9+
String url,
10+
String title,
11+
String thumbnailUrl,
12+
String memo,
13+
LocalDateTime createdAt,
14+
boolean isRead
15+
) {
16+
public static CategoryArticleResponseV3 from(Article article) {
17+
return new CategoryArticleResponseV3(
18+
article.getId(),
19+
article.getUrl(),
20+
article.getTitle(),
21+
article.getThumbnail(),
22+
article.getMemo(),
23+
article.getCreatedAt(),
24+
article.isRead()
25+
);
26+
}
27+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.pinback.application.article.dto.response;
2+
3+
import java.util.List;
4+
5+
public record GetAllArticlesResponseV3(
6+
long totalArticleCount,
7+
long unreadArticleCount,
8+
List<ArticleResponseV3> articles
9+
) {
10+
public static GetAllArticlesResponseV3 of(
11+
long totalArticleCount,
12+
long unreadArticleCount,
13+
List<ArticleResponseV3> articles
14+
) {
15+
return new GetAllArticlesResponseV3(totalArticleCount, unreadArticleCount, articles);
16+
}
17+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.pinback.application.article.dto.response;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.pinback.application.category.dto.response.CategoryResponse;
6+
import com.pinback.domain.article.entity.Article;
7+
8+
public record RemindArticleResponseV3(
9+
long articleId,
10+
String url,
11+
String title,
12+
String thumbnailUrl,
13+
String memo,
14+
LocalDateTime createdAt,
15+
boolean isRead,
16+
boolean isReadAfterRemind,
17+
LocalDateTime remindAt,
18+
CategoryResponse category
19+
) {
20+
public static RemindArticleResponseV3 from(Article article) {
21+
return new RemindArticleResponseV3(
22+
article.getId(),
23+
article.getUrl(),
24+
article.getTitle(),
25+
article.getThumbnail(),
26+
article.getMemo(),
27+
article.getCreatedAt(),
28+
article.isRead(),
29+
article.isReadAfterRemind(),
30+
article.getRemindAt(),
31+
CategoryResponse.from(article.getCategory())
32+
);
33+
}
34+
}

0 commit comments

Comments
 (0)