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 @@ -17,6 +17,7 @@
import com.pinback.application.article.dto.query.PageQuery;
import com.pinback.application.article.dto.response.ArticleDetailResponse;
import com.pinback.application.article.dto.response.ArticlesPageResponse;
import com.pinback.application.article.dto.response.GetAllArticlesResponse;
import com.pinback.application.article.dto.response.ReadArticleResponse;
import com.pinback.application.article.dto.response.TodayRemindResponse;
import com.pinback.application.article.port.in.CreateArticlePort;
Expand Down Expand Up @@ -77,13 +78,13 @@ public ResponseDto<ArticleDetailResponse> checkArticleExists(

@Operation(summary = "모든 아티클 조회", description = "사용자의 모든 아티클을 페이징으로 조회합니다")
@GetMapping
public ResponseDto<ArticlesPageResponse> getAllArticles(
public ResponseDto<GetAllArticlesResponse> getAllArticles(
@Parameter(hidden = true) @CurrentUser User user,
@Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
@Parameter(description = "페이지 크기") @RequestParam(defaultValue = "8") int size
) {
PageQuery query = new PageQuery(page, size);
ArticlesPageResponse response = getArticlePort.getAllArticles(user, query);
GetAllArticlesResponse response = getArticlePort.getAllArticles(user, query);
return ResponseDto.ok(response);
}

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

import java.util.List;

public record GetAllArticlesResponse(
long totalArticle,
long totalUnreadArticle,
boolean isNewUser,
List<ArticleResponse> articles
) {
public static GetAllArticlesResponse of(long totalArticle, long totalUnreadArticle, boolean isNewUser, List<ArticleResponse> articles) {
return new GetAllArticlesResponse(totalArticle, totalUnreadArticle, isNewUser, articles);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.pinback.application.article.dto.query.PageQuery;
import com.pinback.application.article.dto.response.ArticleDetailResponse;
import com.pinback.application.article.dto.response.ArticlesPageResponse;
import com.pinback.application.article.dto.response.GetAllArticlesResponse;
import com.pinback.application.article.dto.response.RemindArticlesResponse;
import com.pinback.application.article.dto.response.TodayRemindResponse;
import com.pinback.domain.user.entity.User;
Expand All @@ -14,7 +15,7 @@ public interface GetArticlePort {

ArticleDetailResponse checkArticleExists(User user, String url);

ArticlesPageResponse getAllArticles(User user, PageQuery query);
GetAllArticlesResponse getAllArticles(User user, PageQuery query);

ArticlesPageResponse getAllArticlesByCategory(User user, long categoryId, PageQuery query);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.pinback.application.article.dto.response.ArticleDetailResponse;
import com.pinback.application.article.dto.response.ArticleResponse;
import com.pinback.application.article.dto.response.ArticlesPageResponse;
import com.pinback.application.article.dto.response.GetAllArticlesResponse;
import com.pinback.application.article.dto.response.RemindArticleResponse;
import com.pinback.application.article.dto.response.TodayRemindResponse;
import com.pinback.application.article.port.in.GetArticlePort;
Expand Down Expand Up @@ -47,17 +48,20 @@ public ArticleDetailResponse checkArticleExists(User user, String url) {
}

@Override
public ArticlesPageResponse getAllArticles(User user, PageQuery query) {
public GetAllArticlesResponse getAllArticles(User user, PageQuery query) {
ArticlesWithUnreadCountDto result = articleGetServicePort.findAll(
user, PageRequest.of(query.pageNumber(), query.pageSize()));

List<ArticleResponse> articleResponses = result.article().stream()
.map(ArticleResponse::from)
.toList();

return ArticlesPageResponse.of(
boolean isNewUser = user.isNewUser(LocalDateTime.now());

return GetAllArticlesResponse.of(
result.article().getTotalElements(),
result.unReadCount(),
isNewUser,
articleResponses
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.pinback.application.article.dto.query.PageQuery;
import com.pinback.application.article.dto.response.ArticleDetailResponse;
import com.pinback.application.article.dto.response.ArticlesPageResponse;
import com.pinback.application.article.dto.response.GetAllArticlesResponse;
import com.pinback.application.article.dto.response.TodayRemindResponse;
import com.pinback.application.article.port.out.ArticleGetServicePort;
import com.pinback.application.category.port.in.GetCategoryPort;
Expand All @@ -50,6 +51,7 @@ class GetArticleUsecaseTest extends ApplicationTestBase {
void setUp() {
user = user();
ReflectionTestUtils.setField(user, "id", java.util.UUID.randomUUID());
ReflectionTestUtils.setField(user, "createdAt", LocalDateTime.now().minusDays(5));
category = categoryWithName(user, "테스트 카테고리");
ReflectionTestUtils.setField(category, "id", 1L);
article = articleWithDate(user, "https://test.com", category, LocalDateTime.of(2025, 8, 20, 15, 0));
Expand Down Expand Up @@ -119,10 +121,11 @@ void getAllArticles_Success() {
when(articleGetServicePort.findAll(user, pageRequest)).thenReturn(dto);

// when
ArticlesPageResponse response = getArticleUsecase.getAllArticles(user, pageQuery);
GetAllArticlesResponse response = getArticleUsecase.getAllArticles(user, pageQuery);

// then
assertThat(response.articles()).hasSize(5);
assertThat(response.isNewUser()).isNotNull();
verify(articleGetServicePort).findAll(user, pageRequest);
}

Expand Down
5 changes: 5 additions & 0 deletions domain/src/main/java/com/pinback/domain/user/entity/User.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.pinback.domain.user.entity;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.UUID;

Expand Down Expand Up @@ -54,4 +55,8 @@ public void increaseAcornCount(Long count) {
public void updateRemindDefault(LocalTime newRemindDefault) {
this.remindDefault = newRemindDefault;
}

public boolean isNewUser(LocalDateTime now) {
return getCreatedAt().isAfter(now.minusDays(3));
}
Comment on lines +59 to +61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Clarify 3-day boundary and guard against null createdAt

Spec says “3일이 지났는지”; current isAfter(now.minusDays(3)) treats exactly 72h as NOT new and NPEs if createdAt is null. Confirm boundary (strict vs inclusive). If inclusive “within first 3 days” is intended, use !isBefore(threshold) and null-guard.

Apply:

- public boolean isNewUser(LocalDateTime now) {
-   return getCreatedAt().isAfter(now.minusDays(3));
- }
+ public boolean isNewUser(LocalDateTime now) {
+   LocalDateTime createdAt = getCreatedAt();
+   if (createdAt == null) return false; // or true based on biz rule
+   LocalDateTime threshold = now.minusDays(3);
+   // true when createdAt is within the last 3 days (inclusive of exactly 3 days ago)
+   return !createdAt.isBefore(threshold);
+ }

If the business rule is “strictly more than 3 days have passed,” then rename method or invert accordingly:

  • hasPassed3Days = createdAt.isBefore(now.minusDays(3)) (strict)
  • isNewUser = !hasPassed3Days

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate User.java
user_file=$(fd -t f -p 'User.java' domain/src/main/java/com/pinback/domain/user/entity)
echo "User file: $user_file"

# Show createdAt field and surrounding annotations
echo -e "\n=== Field Declaration and Annotations ==="
rg -nP '^(?:@.+\r?\n)*\s*private\s+LocalDateTime\s+createdAt' "$user_file"

# Show getCreatedAt() method
echo -e "\n=== getCreatedAt() Method ==="
rg -nP 'public\s+LocalDateTime\s+getCreatedAt' -C2 "$user_file"

# Show isNewUser() method context
echo -e "\n=== isNewUser() Method ==="
rg -nP 'public\s+boolean\s+isNewUser' -C3 "$user_file"

Length of output: 549


🏁 Script executed:

#!/bin/bash
# Locate User.java without exiting on no match
file=$(find domain/src/main/java/com/pinback/domain/user/entity -type f -name User.java)
echo "User file: $file"

echo -e "\n=== Class Declaration ==="
rg -nP 'class\s+User' -C2 "$file" || true

echo -e "\n=== createdAt Field or Inheritance ==="
# Check for direct declaration or inherited auditing fields
rg -nP 'LocalDateTime\s+createdAt' -C2 "$file" || true
rg -nP 'extends?\s+\w+' -C1 "$file" || true

echo -e "\n=== getCreatedAt() Method ==="
rg -nP 'public\s+LocalDateTime\s+getCreatedAt' -C3 "$file" || true

echo -e "\n=== isNewUser() Method ==="
rg -nP 'public\s+boolean\s+isNewUser' -C3 "$file" || true

Length of output: 1500


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate BaseEntity.java
base_file=$(fd -t f -p BaseEntity.java domain/src/main/java/com/pinback)
echo "BaseEntity file: $base_file"

# Show class declaration
echo -e "\n=== Class Declaration ==="
rg -nP 'class\s+BaseEntity' -C2 "$base_file" || true

# Show createdAt field and annotations
echo -e "\n=== createdAt Field Declaration ==="
rg -nP 'LocalDateTime\s+createdAt' -C2 "$base_file" || true

# Show getCreatedAt() method implementation
echo -e "\n=== getCreatedAt() Method ==="
rg -nP 'public\s+LocalDateTime\s+getCreatedAt' -C3 "$base_file" || true

# Check for JPA auditing annotations on createdAt
echo -e "\n=== JPA Auditing Annotations (@CreatedDate) ==="
rg -nP '@CreatedDate' -C2 "$base_file" || true

Length of output: 1489


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="domain/src/main/java/com/pinback/domain/common/BaseEntity.java"
echo "=== Searching for getCreatedAt definition in $file ==="
rg -nP 'public\s+LocalDateTime\s+getCreatedAt' -C3 "$file" || true

echo -e "\n=== Showing first 80 lines of $file ==="
sed -n '1,80p' "$file"

Length of output: 1433


Guard null createdAt and clarify 3-day boundary
Current isNewUser(now) will NPE if createdAt is null and treats exactly 72 hours as “not new.” Add a null check and use an inclusive threshold if you intend “within first 3 days”:

 public boolean isNewUser(LocalDateTime now) {
-   return getCreatedAt().isAfter(now.minusDays(3));
+   LocalDateTime createdAt = getCreatedAt();
+   if (createdAt == null) {
+     return false; // or true per business rule
+   }
+   LocalDateTime threshold = now.minusDays(3);
+   // inclusive: true when createdAt is within last 3 days (>= exactly 3 days ago)
+   return !createdAt.isBefore(threshold);
 }
🤖 Prompt for AI Agents
In domain/src/main/java/com/pinback/domain/user/entity/User.java around lines
59-61, the isNewUser(LocalDateTime now) method can NPE if createdAt is null and
currently treats exactly 72 hours as not new; add a null check to return false
when createdAt is null and change the comparison to include the 3-day boundary
(e.g., compute threshold = now.minusDays(3) and return
!createdAt.isBefore(threshold) or use isAfter(threshold) || isEqual(threshold))
so users created exactly 3 days ago are considered new.

}