Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
package com.pinback.api.article.controller;

import java.time.LocalDateTime;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.pinback.api.article.dto.request.ArticleCreateRequest;
import com.pinback.application.article.dto.query.PageQuery;
import com.pinback.application.article.dto.response.ArticleCountInfoResponse;
import com.pinback.application.article.dto.response.ArticleDetailResponseV3;
import com.pinback.application.article.dto.response.ArticlesPageResponseV3;
import com.pinback.application.article.dto.response.GetAllArticlesResponseV3;
import com.pinback.application.article.dto.response.TodayRemindResponseV3;
import com.pinback.application.article.port.in.CreateArticlePort;
import com.pinback.application.article.port.in.GetArticlePort;
import com.pinback.domain.user.entity.User;
import com.pinback.infrastructure.article.service.ArticleUpdateService;
import com.pinback.shared.annotation.CurrentUser;
Expand All @@ -26,6 +38,7 @@
@Tag(name = "ArticleV3", description = "아티클 관리 API V3")
public class ArticleControllerV3 {
private final CreateArticlePort createArticlePort;
private final GetArticlePort getArticlePort;
private final ArticleUpdateService articleMetadataUpdateService;

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

@Operation(summary = "아티클 상세 조회 V3", description = "아티클의 상세 정보를 조회합니다")
@GetMapping("/{articleId}")
public ResponseDto<ArticleDetailResponseV3> getArticleDetail(
@Parameter(hidden = true) @CurrentUser User user,
@Parameter(description = "아티클 ID") @PathVariable Long articleId
) {
ArticleDetailResponseV3 response = getArticlePort.getArticleDetailWithMetadata(user, articleId);
return ResponseDto.ok(response);
}

@Operation(summary = "리마인드 아티클 조회 v3", description = "오늘 리마인드할 아티클을 읽음/안읽음 상태별로 조회합니다.")
@GetMapping("/remind")
public ResponseDto<TodayRemindResponseV3> getRemindArticlesV3(
@Parameter(hidden = true) @CurrentUser User user,
@Parameter(description = "현재 시간", example = "2026-02-13T10:00:00") @RequestParam LocalDateTime now,
@Parameter(description = "읽음 상태 (true: 읽음, false: 안읽음)", example = "true") @RequestParam(name = "read-status") boolean readStatus,
@Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
@Parameter(description = "페이지 크기") @RequestParam(defaultValue = "8") int size
) {
PageQuery query = new PageQuery(page, size);
TodayRemindResponseV3 response = getArticlePort.getRemindArticlesV3(user, now, readStatus, query);
return ResponseDto.ok(response);
}

@Operation(summary = "리마인드 아티클 읽음/안읽음 개수 조회 v3", description = "오늘 리마인드할 아티클의 읽음/안읽음 개수를 반환합니다.")
@GetMapping("/remind/count")
public ResponseDto<ArticleCountInfoResponse> getRemindArticlesInfo(
@Parameter(hidden = true) @CurrentUser User user,
@Parameter(description = "현재 시간", example = "2026-02-13T10:00:00") @RequestParam LocalDateTime now
) {
ArticleCountInfoResponse response = getArticlePort.getRemindArticlesInfo(user, now);
return ResponseDto.ok(response);
}

@Operation(summary = "나의 북마크 전체 아티클 조회 V3", description = "사용자의 모든 아티클을 페이징으로 조회합니다.")
@GetMapping
public ResponseDto<GetAllArticlesResponseV3> getAllArticles(
@Parameter(hidden = true) @CurrentUser User user,
@Parameter(description = "읽음 상태 (생략: 전체보기, false: 안읽음)", example = "false") @RequestParam(name = "read-status", required = false) Boolean readStatus,
@Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
@Parameter(description = "페이지 크기") @RequestParam(defaultValue = "8") int size
) {
PageQuery query = new PageQuery(page, size);
GetAllArticlesResponseV3 response = getArticlePort.getAllArticlesV3(user, readStatus, query);
return ResponseDto.ok(response);
}

@Operation(summary = "나의 북마크 아티클 전체보기/안읽음 개수 조회 v3", description = "나의 북마크 아티클의 전체보기/안읽음 개수를 반환합니다.")
@GetMapping("/count")
public ResponseDto<ArticleCountInfoResponse> getArticlesInfo(
@Parameter(hidden = true) @CurrentUser User user
) {
ArticleCountInfoResponse response = getArticlePort.getAllArticlesInfo(user);
return ResponseDto.ok(response);
}

@Operation(summary = "나의 북마크 카테고리별 아티클 조회 V3", description = "특정 카테고리의 아티클 전체 목록을 조회합니다.")
@GetMapping("/category")
public ResponseDto<ArticlesPageResponseV3> getAllArticlesByCategory(
@Parameter(hidden = true) @CurrentUser User user,
@Parameter(description = "카테고리 ID") @RequestParam(name = "category-id") Long categoryId,
@Parameter(description = "읽음 상태 (생략시: 전체보기, false: 안읽음)", example = "false") @RequestParam(name = "read-status", required = false) Boolean readStatus,
@Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
@Parameter(description = "페이지 크기") @RequestParam(defaultValue = "8") int size
) {
PageQuery query = new PageQuery(page, size);
ArticlesPageResponseV3 response = getArticlePort.getAllArticlesByCategoryV3(user, categoryId, readStatus,
query);
return ResponseDto.ok(response);
}

@Operation(summary = "나의 북마크 카테고리별 아티클 전체보기/안읽음 개수 조회 v3", description = "나의 북마크에서 특정 카테고리 아티클의 전체보기/안읽음 개수를 반환합니다.")
@GetMapping("/category/count")
public ResponseDto<ArticleCountInfoResponse> getArticlesInfoByCategory(
@Parameter(hidden = true) @CurrentUser User user,
@Parameter(description = "카테고리 ID") @RequestParam(name = "category-id") Long categoryId

) {
ArticleCountInfoResponse response = getArticlePort.getAllArticlesInfoByCategoryV3(user, categoryId);
return ResponseDto.ok(response);
}

// 기존 아티클 메타데이터 처리 후 삭제 예정
@PostMapping("/metadata")
public ResponseDto<Void> migrateMetadata() {
articleMetadataUpdateService.migrateMissingMetadata();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.pinback.application.article.dto;

public record ArticleCountInfoDtoV3(
long totalCount,
long readCount,
long unreadCount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.pinback.application.article.dto;

import org.springframework.data.domain.Page;

import com.pinback.domain.article.entity.Article;

public record ArticlesWithCountDto(
long totalCount,
long unreadCount,
Page<Article> article
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.pinback.application.article.dto.response;

public record ArticleCountInfoResponse(
long totalArticleCount,
long readArticleCount,
long unreadArticleCount
) {
public static ArticleCountInfoResponse of(
long totalArticleCount,
long readArticleCount,
long unreadArticleCount
) {
return new ArticleCountInfoResponse(totalArticleCount, readArticleCount, unreadArticleCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.pinback.application.article.dto.response;

import java.time.LocalDateTime;

import com.pinback.application.category.dto.response.CategoryResponse;
import com.pinback.domain.article.entity.Article;

public record ArticleDetailResponseV3(
long articleId,
String url,
String title,
String thumbnailUrl,
String memo,
LocalDateTime remindAt,
LocalDateTime createdAt,
CategoryResponse categoryResponse
) {
public static ArticleDetailResponseV3 from(Article article) {
return new ArticleDetailResponseV3(
article.getId(),
article.getUrl(),
article.getTitle(),
article.getThumbnail(),
article.getMemo(),
article.getRemindAt(),
article.getCreatedAt(),
CategoryResponse.from(article.getCategory())
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.pinback.application.article.dto.response;

import java.time.LocalDateTime;

import com.pinback.application.category.dto.response.CategoryResponse;
import com.pinback.domain.article.entity.Article;

public record ArticleResponseV3(
long articleId,
String url,
String title,
String thumbnailUrl,
String memo,
LocalDateTime createdAt,
boolean isRead,
CategoryResponse category
) {
public static ArticleResponseV3 from(Article article) {
return new ArticleResponseV3(
article.getId(),
article.getUrl(),
article.getTitle(),
article.getThumbnail(),
article.getMemo(),
article.getCreatedAt(),
article.isRead(),
CategoryResponse.from(article.getCategory())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.pinback.application.article.dto.response;

import java.util.List;

public record ArticlesPageResponseV3(
long totalArticleCount,
long unreadArticleCount,
String categoryName,
List<CategoryArticleResponseV3> articles
) {
public static ArticlesPageResponseV3 of(long totalArticleCount, long unreadArticleCount, String categoryName,
List<CategoryArticleResponseV3> articles) {
return new ArticlesPageResponseV3(totalArticleCount, unreadArticleCount, categoryName, articles);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.pinback.application.article.dto.response;

import java.time.LocalDateTime;

import com.pinback.domain.article.entity.Article;

public record CategoryArticleResponseV3(
long articleId,
String url,
String title,
String thumbnailUrl,
String memo,
LocalDateTime createdAt,
boolean isRead
) {
public static CategoryArticleResponseV3 from(Article article) {
return new CategoryArticleResponseV3(
article.getId(),
article.getUrl(),
article.getTitle(),
article.getThumbnail(),
article.getMemo(),
article.getCreatedAt(),
article.isRead()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.pinback.application.article.dto.response;

import java.util.List;

public record GetAllArticlesResponseV3(
long totalArticleCount,
long unreadArticleCount,
List<ArticleResponseV3> articles
) {
public static GetAllArticlesResponseV3 of(
long totalArticleCount,
long unreadArticleCount,
List<ArticleResponseV3> articles
) {
return new GetAllArticlesResponseV3(totalArticleCount, unreadArticleCount, articles);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.pinback.application.article.dto.response;

import java.time.LocalDateTime;

import com.pinback.application.category.dto.response.CategoryResponse;
import com.pinback.domain.article.entity.Article;

public record RemindArticleResponseV3(
long articleId,
String url,
String title,
String thumbnailUrl,
String memo,
LocalDateTime createdAt,
boolean isRead,
boolean isReadAfterRemind,
LocalDateTime remindAt,
CategoryResponse category
) {
public static RemindArticleResponseV3 from(Article article) {
return new RemindArticleResponseV3(
article.getId(),
article.getUrl(),
article.getTitle(),
article.getThumbnail(),
article.getMemo(),
article.getCreatedAt(),
article.isRead(),
article.isReadAfterRemind(),
article.getRemindAt(),
CategoryResponse.from(article.getCategory())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.pinback.application.article.dto.response;

import java.util.List;

public record TodayRemindResponseV3(
boolean hasNext,
long totalArticleCount,
long readArticleCount,
long unreadArticleCount,
List<RemindArticleResponseV3> articles
) {
public static TodayRemindResponseV3 of(
boolean hasNext,
long totalArticleCount,
long readArticleCount,
long unreadArticleCount,
List<RemindArticleResponseV3> articles
) {
return new TodayRemindResponseV3(hasNext, totalArticleCount, readArticleCount, unreadArticleCount, articles);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
import java.time.LocalDateTime;

import com.pinback.application.article.dto.query.PageQuery;
import com.pinback.application.article.dto.response.ArticleCountInfoResponse;
import com.pinback.application.article.dto.response.ArticleDetailResponse;
import com.pinback.application.article.dto.response.ArticleDetailResponseV3;
import com.pinback.application.article.dto.response.ArticlesPageResponse;
import com.pinback.application.article.dto.response.ArticlesPageResponseV3;
import com.pinback.application.article.dto.response.GetAllArticlesResponse;
import com.pinback.application.article.dto.response.GetAllArticlesResponseV3;
import com.pinback.application.article.dto.response.TodayRemindResponse;
import com.pinback.application.article.dto.response.TodayRemindResponseV2;
import com.pinback.application.article.dto.response.TodayRemindResponseV3;
import com.pinback.domain.user.entity.User;

public interface GetArticlePort {
Expand All @@ -24,4 +29,18 @@ public interface GetArticlePort {
TodayRemindResponse getRemindArticles(User user, LocalDateTime now, boolean readStatus, PageQuery query);

TodayRemindResponseV2 getRemindArticlesV2(User user, LocalDateTime now, boolean readStatus, PageQuery query);

ArticleDetailResponseV3 getArticleDetailWithMetadata(User user, long articleId);

TodayRemindResponseV3 getRemindArticlesV3(User user, LocalDateTime now, boolean readStatus, PageQuery query);

ArticleCountInfoResponse getRemindArticlesInfo(User user, LocalDateTime now);

GetAllArticlesResponseV3 getAllArticlesV3(User user, Boolean readStatus, PageQuery query);

ArticleCountInfoResponse getAllArticlesInfo(User user);

ArticlesPageResponseV3 getAllArticlesByCategoryV3(User user, long categoryId, Boolean readStatus, PageQuery query);

ArticleCountInfoResponse getAllArticlesInfoByCategoryV3(User user, long categoryId);
}
Loading