diff --git a/module-domain/src/main/java/com/depromeet/memory/Timeline.java b/module-domain/src/main/java/com/depromeet/memory/Timeline.java new file mode 100644 index 00000000..27773c2b --- /dev/null +++ b/module-domain/src/main/java/com/depromeet/memory/Timeline.java @@ -0,0 +1,29 @@ +package com.depromeet.memory; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class Timeline { + private List timelineContents; + private int pageSize; + private LocalDate cursorRecordAt; + private boolean hasNext; + + @Builder + public Timeline( + List timelineContents, + int pageSize, + LocalDate cursorRecordAt, + boolean hasNext) { + this.timelineContents = timelineContents != null ? timelineContents : new ArrayList<>(); + this.pageSize = pageSize != 0 ? pageSize : 10; + this.cursorRecordAt = cursorRecordAt; + this.hasNext = hasNext; + } +} diff --git a/module-independent/src/main/java/com/depromeet/dto/response/CustomSliceResponse.java b/module-independent/src/main/java/com/depromeet/dto/response/CustomSliceResponse.java index ae8b2478..1a093059 100644 --- a/module-independent/src/main/java/com/depromeet/dto/response/CustomSliceResponse.java +++ b/module-independent/src/main/java/com/depromeet/dto/response/CustomSliceResponse.java @@ -2,7 +2,8 @@ import lombok.Builder; -public record CustomSliceResponse(T content, int pageNumber, int pageSize, boolean hasNext) { +public record CustomSliceResponse( + T content, int pageSize, String cursorRecordAt, boolean hasNext) { @Builder public CustomSliceResponse {} } diff --git a/module-infrastructure/persistence-database/src/main/java/com/depromeet/memory/repository/MemoryRepository.java b/module-infrastructure/persistence-database/src/main/java/com/depromeet/memory/repository/MemoryRepository.java index 274849be..a33d1b4c 100644 --- a/module-infrastructure/persistence-database/src/main/java/com/depromeet/memory/repository/MemoryRepository.java +++ b/module-infrastructure/persistence-database/src/main/java/com/depromeet/memory/repository/MemoryRepository.java @@ -1,11 +1,10 @@ package com.depromeet.memory.repository; import com.depromeet.memory.Memory; +import com.depromeet.memory.Timeline; import java.time.LocalDate; import java.util.List; import java.util.Optional; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; public interface MemoryRepository { Memory save(Memory memory); @@ -16,11 +15,9 @@ public interface MemoryRepository { Optional update(Long memoryId, Memory memoryUpdate); - Slice findPrevMemoryByMemberId( - Long memberId, Long cursorId, Pageable pageable, LocalDate recordAt); + Timeline findPrevMemoryByMemberId(Long memberId, LocalDate cursorRecordAt, LocalDate recordAt); - Slice findNextMemoryByMemberId( - Long memberId, Long cursorId, Pageable pageable, LocalDate recordAt); + Timeline findNextMemoryByMemberId(Long memberId, LocalDate cursorRecordAt, LocalDate recordAt); List getCalendarByYearAndMonth(Long memberId, Integer year, Short month); } diff --git a/module-infrastructure/persistence-database/src/main/java/com/depromeet/memory/repository/MemoryRepositoryImpl.java b/module-infrastructure/persistence-database/src/main/java/com/depromeet/memory/repository/MemoryRepositoryImpl.java index 344be10d..bb1a5bd3 100644 --- a/module-infrastructure/persistence-database/src/main/java/com/depromeet/memory/repository/MemoryRepositoryImpl.java +++ b/module-infrastructure/persistence-database/src/main/java/com/depromeet/memory/repository/MemoryRepositoryImpl.java @@ -7,6 +7,7 @@ import static com.depromeet.pool.entity.QPoolEntity.poolEntity; import com.depromeet.memory.Memory; +import com.depromeet.memory.Timeline; import com.depromeet.memory.entity.MemoryEntity; import com.depromeet.memory.entity.QMemoryEntity; import com.querydsl.core.types.dsl.BooleanExpression; @@ -18,9 +19,9 @@ import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Repository; @Slf4j @@ -78,59 +79,75 @@ public Optional update(Long memoryId, Memory memoryUpdate) { .map(entity -> entity.update(MemoryEntity.from(memoryUpdate)).toModel()); } - // ---- 날짜 선택 후 위아래 무한 스크롤 구현 - @Override - public Slice findPrevMemoryByMemberId( - Long memberId, Long cursorId, Pageable pageable, LocalDate recordAt) { + public Timeline findPrevMemoryByMemberId( + Long memberId, LocalDate cursorRecordAt, LocalDate recordAt) { + Pageable pageable = PageRequest.of(0, 10, Sort.Direction.DESC, "recordAt"); List result = queryFactory .selectFrom(memory) .where( memory.member.id.eq(memberId), - ltCursorId(cursorId), + ltCursorRecordAt(cursorRecordAt), loeRecordAt(recordAt)) .limit(pageable.getPageSize() + 1) .orderBy(memory.recordAt.desc()) .fetch(); List content = toModel(result); - boolean hasPrev = false; + boolean hasNext = false; + LocalDate nextMemoryRecordAt = null; if (content.size() > pageable.getPageSize()) { content = new ArrayList<>(content); content.removeLast(); - hasPrev = true; + hasNext = true; + Memory lastMemory = content.getLast(); + nextMemoryRecordAt = lastMemory.getRecordAt(); } - return new SliceImpl<>(content, pageable, hasPrev); + return Timeline.builder() + .timelineContents(content) + .pageSize(10) + .cursorRecordAt(nextMemoryRecordAt) + .hasNext(hasNext) + .build(); } @Override - public Slice findNextMemoryByMemberId( - Long memberId, Long cursorId, Pageable pageable, LocalDate recordAt) { + public Timeline findNextMemoryByMemberId( + Long memberId, LocalDate cursorRecordAt, LocalDate recordAt) { + Pageable pageable = PageRequest.of(0, 10, Sort.Direction.DESC, "recordAt"); + List result = queryFactory .selectFrom(memory) .where( memory.member.id.eq(memberId), - gtCursorId(cursorId), + gtCursorRecordAt(cursorRecordAt), goeRecordAt(recordAt)) .limit(pageable.getPageSize() + 1) .orderBy(memory.recordAt.asc()) .fetch(); - List content = toModel(result); boolean hasNext = false; + LocalDate nextMemoryRecordAt = null; if (content.size() > pageable.getPageSize()) { content = new ArrayList<>(content); content.removeLast(); hasNext = true; + Memory lastMemory = content.getLast(); + nextMemoryRecordAt = lastMemory.getRecordAt(); } content = content.reversed(); - return new SliceImpl<>(content, pageable, hasNext); + return Timeline.builder() + .timelineContents(content) + .pageSize(10) + .cursorRecordAt(nextMemoryRecordAt) + .hasNext(hasNext) + .build(); } @Override @@ -161,18 +178,18 @@ private BooleanExpression loeRecordAt(LocalDate recordAt) { return memory.recordAt.loe(recordAt); } - private BooleanExpression ltCursorId(Long cursorId) { - if (cursorId == null) { + private BooleanExpression ltCursorRecordAt(LocalDate cursorRecordAt) { + if (cursorRecordAt == null) { return null; } - return memory.id.lt(cursorId); + return memory.recordAt.lt(cursorRecordAt); } - private BooleanExpression gtCursorId(Long cursorId) { - if (cursorId == null) { + private BooleanExpression gtCursorRecordAt(LocalDate cursorRecordAt) { + if (cursorRecordAt == null) { return null; } - return memory.id.gt(cursorId); + return memory.recordAt.gt(cursorRecordAt); } private BooleanExpression goeRecordAt(LocalDate recordAt) { diff --git a/module-infrastructure/persistence-database/src/test/java/com/depromeet/memory/repository/MemoryRepositoryTest.java b/module-infrastructure/persistence-database/src/test/java/com/depromeet/memory/repository/MemoryRepositoryTest.java index ecb1169c..b0b939f3 100644 --- a/module-infrastructure/persistence-database/src/test/java/com/depromeet/memory/repository/MemoryRepositoryTest.java +++ b/module-infrastructure/persistence-database/src/test/java/com/depromeet/memory/repository/MemoryRepositoryTest.java @@ -11,6 +11,7 @@ import com.depromeet.member.repository.MemberRepositoryImpl; import com.depromeet.memory.Memory; import com.depromeet.memory.MemoryDetail; +import com.depromeet.memory.Timeline; import com.querydsl.jpa.impl.JPAQueryFactory; import java.time.LocalDate; import java.util.List; @@ -20,18 +21,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; import org.springframework.test.context.junit.jupiter.SpringExtension; @DataJpaTest @Import(TestQueryDslConfig.class) @ExtendWith(SpringExtension.class) public class MemoryRepositoryTest { - private Pageable pageable; - @Autowired private JPAQueryFactory queryFactory; @Autowired private MemoryJpaRepository memoryJpaRepository; private MemoryRepositoryImpl memoryRepositoryImpl; @@ -45,8 +40,6 @@ public class MemoryRepositoryTest { @BeforeEach void setUp() { - pageable = getPageable(); - memberRepositoryImpl = new MemberRepositoryImpl(memberJpaRepository); memoryRepositoryImpl = new MemoryRepositoryImpl(queryFactory, memoryJpaRepository); memoryDetailRepositoryImpl = new MemoryDetailRepositoryImpl(memoryDetailJpaRepository); @@ -62,21 +55,17 @@ void setUp() { } } - private Pageable getPageable() { - return PageRequest.of(0, 30, Sort.by(Sort.Order.desc("recordAt"))); - } - @Test void findPrevMemoryByMemberId로_최근_날짜_이전_30일_recordAt_Desc로_가져오는지_테스트() { // when - Slice resultSlice = - memoryRepositoryImpl.findPrevMemoryByMemberId(member.getId(), null, pageable, null); - List result = resultSlice.getContent(); + Timeline timelines = + memoryRepositoryImpl.findPrevMemoryByMemberId(member.getId(), null, null); + List result = timelines.getTimelineContents(); Memory lastMemory = result.getLast(); // then - assertThat(result.size()).isEqualTo(30); - assertThat(lastMemory.getRecordAt()).isEqualTo(startRecordAt.minusDays(30)); + assertThat(result.size()).isEqualTo(10); + assertThat(lastMemory.getRecordAt()).isEqualTo(startRecordAt.minusDays(10)); } @Test @@ -85,15 +74,14 @@ private Pageable getPageable() { LocalDate recordAt = LocalDate.of(2024, 8, 31); // when - Slice resultSlice = - memoryRepositoryImpl.findPrevMemoryByMemberId( - member.getId(), null, pageable, recordAt); - List result = resultSlice.getContent(); + Timeline timelines = + memoryRepositoryImpl.findPrevMemoryByMemberId(member.getId(), null, recordAt); + List result = timelines.getTimelineContents(); Memory lastMemory = result.getLast(); // then - assertThat(result.size()).isEqualTo(30); - assertThat(lastMemory.getRecordAt()).isEqualTo(recordAt.minusDays(29)); + assertThat(result.size()).isEqualTo(10); + assertThat(lastMemory.getRecordAt()).isEqualTo(recordAt.minusDays(9)); } @Test @@ -101,22 +89,21 @@ private Pageable getPageable() { // given LocalDate recordAt = LocalDate.of(2024, 8, 31); - Slice initResultSlice = - memoryRepositoryImpl.findPrevMemoryByMemberId( - member.getId(), null, pageable, recordAt); + Timeline initTimelines = + memoryRepositoryImpl.findPrevMemoryByMemberId(member.getId(), null, recordAt); - List initResultSliceList = initResultSlice.getContent(); - Memory lastDate = initResultSliceList.getLast(); + List timelineContents = initTimelines.getTimelineContents(); + Memory lastDate = timelineContents.getLast(); // when - Slice resultSlice = + Timeline timelines = memoryRepositoryImpl.findPrevMemoryByMemberId( - member.getId(), lastDate.getId(), pageable, null); - List result = resultSlice.getContent(); + member.getId(), lastDate.getRecordAt(), null); + List result = timelines.getTimelineContents(); // then - assertThat(result.size()).isEqualTo(30); - assertThat(result.getLast().getRecordAt()).isEqualTo(lastDate.getRecordAt().minusDays(30)); + assertThat(result.size()).isEqualTo(10); + assertThat(result.getLast().getRecordAt()).isEqualTo(lastDate.getRecordAt().minusDays(10)); } @Test @@ -124,21 +111,20 @@ private Pageable getPageable() { // given LocalDate recordAt = LocalDate.of(2024, 8, 31); - Slice initResultSlice = - memoryRepositoryImpl.findPrevMemoryByMemberId( - member.getId(), null, pageable, recordAt); + Timeline initTimeline = + memoryRepositoryImpl.findPrevMemoryByMemberId(member.getId(), null, recordAt); - List initResultSliceList = initResultSlice.getContent(); - Memory firstDate = initResultSliceList.getFirst(); + List initTimelineContents = initTimeline.getTimelineContents(); + Memory firstDate = initTimelineContents.getFirst(); // when - Slice resultSlice = + Timeline resultSlice = memoryRepositoryImpl.findNextMemoryByMemberId( - member.getId(), firstDate.getId(), pageable, null); - List result = resultSlice.getContent(); + member.getId(), firstDate.getRecordAt(), null); + List result = resultSlice.getTimelineContents(); // then - assertThat(result.size()).isEqualTo(30); - assertThat(result.getFirst().getRecordAt()).isEqualTo(firstDate.getRecordAt().plusDays(30)); + assertThat(result.size()).isEqualTo(10); + assertThat(result.getFirst().getRecordAt()).isEqualTo(firstDate.getRecordAt().plusDays(10)); } } diff --git a/module-infrastructure/persistence-database/src/test/resources/application.yml b/module-infrastructure/persistence-database/src/test/resources/application.yml index 94b7319a..45de6a26 100644 --- a/module-infrastructure/persistence-database/src/test/resources/application.yml +++ b/module-infrastructure/persistence-database/src/test/resources/application.yml @@ -11,9 +11,4 @@ spring: jpa: generate-ddl: true hibernate: - ddl-auto: create - properties: - hibernate: - show_sql: true - format_sql: true - use_sql_comments: true \ No newline at end of file + ddl-auto: create \ No newline at end of file diff --git a/module-presentation/build.gradle b/module-presentation/build.gradle index 1af4c69e..fe2b78f8 100644 --- a/module-presentation/build.gradle +++ b/module-presentation/build.gradle @@ -13,9 +13,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-validation' - - //for test - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' testImplementation 'org.springframework.security:spring-security-test' // swagger diff --git a/module-presentation/src/main/java/com/depromeet/auth/dto/request/AccessTokenDto.java b/module-presentation/src/main/java/com/depromeet/auth/dto/request/AccessTokenDto.java new file mode 100644 index 00000000..a33f8b7b --- /dev/null +++ b/module-presentation/src/main/java/com/depromeet/auth/dto/request/AccessTokenDto.java @@ -0,0 +1,3 @@ +package com.depromeet.auth.dto.request; + +public record AccessTokenDto(String accessToken) {} diff --git a/module-presentation/src/main/java/com/depromeet/auth/dto/response/RefreshTokenDto.java b/module-presentation/src/main/java/com/depromeet/auth/dto/response/RefreshTokenDto.java new file mode 100644 index 00000000..de7b6bbe --- /dev/null +++ b/module-presentation/src/main/java/com/depromeet/auth/dto/response/RefreshTokenDto.java @@ -0,0 +1,8 @@ +package com.depromeet.auth.dto.response; + +import lombok.Builder; + +public record RefreshTokenDto(String refreshToken) { + @Builder + public RefreshTokenDto {} +} diff --git a/module-presentation/src/main/java/com/depromeet/common/ErrorResponse.java b/module-presentation/src/main/java/com/depromeet/common/ErrorResponse.java new file mode 100644 index 00000000..3e7616ee --- /dev/null +++ b/module-presentation/src/main/java/com/depromeet/common/ErrorResponse.java @@ -0,0 +1,31 @@ +package com.depromeet.common; + +import com.depromeet.common.dto.ConstraintViolationError; +import com.depromeet.common.dto.FieldError; +import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.ConstraintViolation; +import java.util.List; +import java.util.Set; +import lombok.Getter; +import org.springframework.validation.BindingResult; + +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ErrorResponse { + private List fieldErrors; + private List constraintViolations; + + public ErrorResponse( + List fieldErrors, List constraintViolations) { + this.fieldErrors = fieldErrors; + this.constraintViolations = constraintViolations; + } + + public static ErrorResponse of(BindingResult bindingResult) { + return new ErrorResponse(FieldError.of(bindingResult), null); + } + + public static ErrorResponse of(Set> constraintViolations) { + return new ErrorResponse(null, ConstraintViolationError.of(constraintViolations)); + } +} diff --git a/module-presentation/src/main/java/com/depromeet/common/GlobalExceptionAdvice.java b/module-presentation/src/main/java/com/depromeet/common/GlobalExceptionAdvice.java index f0bbb174..dc0b7a3d 100644 --- a/module-presentation/src/main/java/com/depromeet/common/GlobalExceptionAdvice.java +++ b/module-presentation/src/main/java/com/depromeet/common/GlobalExceptionAdvice.java @@ -8,14 +8,10 @@ import jakarta.validation.ConstraintViolationException; import jakarta.validation.UnexpectedTypeException; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.validation.Errors; -import org.springframework.validation.FieldError; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingRequestHeaderException; @@ -29,13 +25,7 @@ public class GlobalExceptionAdvice { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity> handleMethodArgumentNotValidException( final MethodArgumentNotValidException ex) { - Errors errors = ex.getBindingResult(); - Map validateDetails = new HashMap<>(); - - for (FieldError error : errors.getFieldErrors()) { - String validKeyName = String.format("valid_%s", error.getField()); - validateDetails.put(validKeyName, error.getDefaultMessage()); - } + ErrorResponse validateDetails = ErrorResponse.of(ex.getBindingResult()); return new ResponseEntity<>( ApiResponse.fail(CommonErrorType.REQUEST_VALIDATION, 400, validateDetails), HttpStatus.BAD_REQUEST); @@ -88,8 +78,9 @@ protected ResponseEntity> handlerConstraintDefinitionException( protected ResponseEntity> handlerConstraintViolationException( final ConstraintViolationException ex) { log.error(ex.getMessage()); + ErrorResponse constraintViolation = ErrorResponse.of(ex.getConstraintViolations()); return new ResponseEntity<>( - ApiResponse.fail(CommonErrorType.VALIDATION_FAILED, 400, ex.toString()), + ApiResponse.fail(CommonErrorType.VALIDATION_FAILED, 400, constraintViolation), HttpStatus.BAD_REQUEST); } diff --git a/module-presentation/src/main/java/com/depromeet/common/dto/ConstraintViolationError.java b/module-presentation/src/main/java/com/depromeet/common/dto/ConstraintViolationError.java new file mode 100644 index 00000000..f2a0a834 --- /dev/null +++ b/module-presentation/src/main/java/com/depromeet/common/dto/ConstraintViolationError.java @@ -0,0 +1,23 @@ +package com.depromeet.common.dto; + +import jakarta.validation.ConstraintViolation; +import java.util.List; +import java.util.Set; +import lombok.Builder; + +public record ConstraintViolationError(String propertyPath, Object rejectedValue, String reason) { + @Builder + public ConstraintViolationError {} + + public static List of(Set> violations) { + return violations.stream() + .map( + violation -> + ConstraintViolationError.builder() + .propertyPath(violation.getPropertyPath().toString()) + .rejectedValue(violation.getInvalidValue().toString()) + .reason(violation.getMessage()) + .build()) + .toList(); + } +} diff --git a/module-presentation/src/main/java/com/depromeet/common/dto/FieldError.java b/module-presentation/src/main/java/com/depromeet/common/dto/FieldError.java new file mode 100644 index 00000000..8b4c782e --- /dev/null +++ b/module-presentation/src/main/java/com/depromeet/common/dto/FieldError.java @@ -0,0 +1,28 @@ +package com.depromeet.common.dto; + +import java.util.List; +import lombok.Builder; +import org.springframework.validation.BindingResult; + +public record FieldError(String field, Object rejectedValue, String reason) { + @Builder + public FieldError {} + + public static List of(BindingResult bindingResult) { + List fieldErrors = + bindingResult.getFieldErrors(); + + return fieldErrors.stream() + .map( + error -> + FieldError.builder() + .field(String.format("valid_%s", error.getField())) + .rejectedValue( + error.getRejectedValue() != null + ? error.getRejectedValue().toString() + : "") + .reason(error.getDefaultMessage()) + .build()) + .toList(); + } +} diff --git a/module-presentation/src/main/java/com/depromeet/image/service/ImageUpdateServiceImpl.java b/module-presentation/src/main/java/com/depromeet/image/service/ImageUpdateServiceImpl.java index 078cf607..c6b6b760 100644 --- a/module-presentation/src/main/java/com/depromeet/image/service/ImageUpdateServiceImpl.java +++ b/module-presentation/src/main/java/com/depromeet/image/service/ImageUpdateServiceImpl.java @@ -67,7 +67,7 @@ private List updateNewImages( for (MultipartFile image : images) { String originImageName = image.getOriginalFilename(); String imageName = generateImageName(originImageName); - updatedImageNames.add(imageName); + updatedImageNames.add(imageName); // 이게 if문 밑으로 내려가야 하지 않을까요? if (existImagesName.contains(imageName)) continue; diff --git a/module-presentation/src/main/java/com/depromeet/memory/api/MemoryController.java b/module-presentation/src/main/java/com/depromeet/memory/api/MemoryController.java index add94d5a..52bc16ef 100644 --- a/module-presentation/src/main/java/com/depromeet/memory/api/MemoryController.java +++ b/module-presentation/src/main/java/com/depromeet/memory/api/MemoryController.java @@ -47,7 +47,7 @@ public ApiResponse update( @GetMapping("/timeline") public ApiResponse timeline( - @LoginMember Long memberId, @ModelAttribute TimelineRequestDto timelineRequestDto) { + @LoginMember Long memberId, TimelineRequestDto timelineRequestDto) { CustomSliceResponse result = memoryFacade.getTimelineByMemberIdAndCursorAndDate(memberId, timelineRequestDto); return ApiResponse.success(MemorySuccessType.GET_TIMELINE_SUCCESS, result); diff --git a/module-presentation/src/main/java/com/depromeet/memory/dto/request/TimelineRequestDto.java b/module-presentation/src/main/java/com/depromeet/memory/dto/request/TimelineRequestDto.java index eca2862a..87777c61 100644 --- a/module-presentation/src/main/java/com/depromeet/memory/dto/request/TimelineRequestDto.java +++ b/module-presentation/src/main/java/com/depromeet/memory/dto/request/TimelineRequestDto.java @@ -1,8 +1,7 @@ package com.depromeet.memory.dto.request; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; import java.time.YearMonth; import lombok.*; import org.springframework.format.annotation.DateTimeFormat; @@ -12,8 +11,9 @@ @NoArgsConstructor @AllArgsConstructor public class TimelineRequestDto { - @Schema(description = "최초 조회 이후 나온 timeline 리스트 중 가장 마지막 요소의 memory PK") - private Long cursorId; + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(description = "최초 조회 이후 나온 timeline 리스트 중 가장 마지막 요소의 memory recordAt") + private LocalDate cursorRecordAt; @DateTimeFormat(pattern = "yyyy-MM") @Schema(description = "조회하고 싶은 날짜, yyyy-MM") @@ -22,9 +22,4 @@ public class TimelineRequestDto { @Builder.Default @Schema(description = "조회하고 싶은 날짜 조회 이후, 날짜 기준 이전 정보를 보고 싶다면 false, 이후 정보를 보고 싶다면 true") private boolean showNewer = false; - - @Min(1) - @NotNull - @Schema(description = "페이지 크기") - private Integer size; } diff --git a/module-presentation/src/main/java/com/depromeet/memory/service/TimelineServiceImpl.java b/module-presentation/src/main/java/com/depromeet/memory/service/TimelineServiceImpl.java index f5200a5f..a0f85e87 100644 --- a/module-presentation/src/main/java/com/depromeet/memory/service/TimelineServiceImpl.java +++ b/module-presentation/src/main/java/com/depromeet/memory/service/TimelineServiceImpl.java @@ -5,6 +5,7 @@ import com.depromeet.image.dto.response.MemoryImagesDto; import com.depromeet.memory.Memory; import com.depromeet.memory.Stroke; +import com.depromeet.memory.Timeline; import com.depromeet.memory.dto.request.TimelineRequestDto; import com.depromeet.memory.dto.response.StrokeResponse; import com.depromeet.memory.dto.response.TimelineResponseDto; @@ -17,10 +18,6 @@ import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -34,34 +31,54 @@ public class TimelineServiceImpl implements TimelineService { @Override public CustomSliceResponse getTimelineByMemberIdAndCursorAndDate( Long memberId, TimelineRequestDto timeline) { - Slice memories = getTimelines(memberId, timeline); - Slice result = memories.map(this::mapToTimelineResponseDto); - return mapToCustomSliceResponse(result); + Timeline timelines = getTimelines(memberId, timeline); + + return mapToCustomSliceResponse(timelines); } - private Slice getTimelines(Long memberId, TimelineRequestDto timeline) { - Pageable pageable = - PageRequest.of(0, timeline.getSize(), Sort.by(Sort.Order.desc("recordAt"))); + private Timeline getTimelines(Long memberId, TimelineRequestDto timeline) { + LocalDate cursorRecordAt = null; + if (timeline.getCursorRecordAt() != null) { + cursorRecordAt = timeline.getCursorRecordAt(); + } LocalDate parsedDate = getLocalDateOrNull(timeline.getDate()); + if (timeline.isShowNewer()) { - return memoryRepository.findNextMemoryByMemberId( - memberId, timeline.getCursorId(), pageable, parsedDate); + return memoryRepository.findNextMemoryByMemberId(memberId, cursorRecordAt, parsedDate); } else { - return memoryRepository.findPrevMemoryByMemberId( - memberId, timeline.getCursorId(), pageable, parsedDate); + return memoryRepository.findPrevMemoryByMemberId(memberId, cursorRecordAt, parsedDate); } } + private CustomSliceResponse mapToCustomSliceResponse(Timeline timelines) { + List result = + timelines.getTimelineContents().stream() + .map(this::mapToTimelineResponseDto) + .toList(); + + return CustomSliceResponse.builder() + .content(result) + .pageSize(timelines.getPageSize()) + .cursorRecordAt(getCursorRecordAtResponse(timelines)) + .hasNext(timelines.isHasNext()) + .build(); + } + private LocalDate getLocalDateOrNull(YearMonth date) { LocalDate lastDayOfMonth = null; - log.info("localDate : {}", date); if (date != null) { lastDayOfMonth = date.atEndOfMonth(); } return lastDayOfMonth; } + private String getCursorRecordAtResponse(Timeline timelines) { + return timelines.getCursorRecordAt() != null + ? timelines.getCursorRecordAt().toString() + : null; + } + @Override public TimelineResponseDto mapToTimelineResponseDto(Memory memory) { return TimelineResponseDto.builder() @@ -72,10 +89,7 @@ public TimelineResponseDto mapToTimelineResponseDto(Memory memory) { .lane(memory.getLane()) .diary(memory.getDiary()) .totalMeter(calculateTotalMeter(memory.getStrokes(), memory.getLane())) - .memoryDetailId( - memory.getMemoryDetail() != null && memory.getMemoryDetail().getId() != null - ? memory.getMemoryDetail().getId() - : null) + .memoryDetailId(getMemoryDetailId(memory)) .item(getItemFromMemoryDetail(memory)) .heartRate(getHeartRateFromMemoryDetail(memory)) .pace(getPaceFromMemoryDetail(memory)) @@ -132,6 +146,12 @@ private Integer calculateTotalMeter(List strokes, Short lane) { return totalMeter; } + private Long getMemoryDetailId(Memory memory) { + return memory.getMemoryDetail() != null && memory.getMemoryDetail().getId() != null + ? memory.getMemoryDetail().getId() + : null; + } + private List strokeToDto(List strokes) { if (strokes == null || strokes.isEmpty()) return null; @@ -169,15 +189,4 @@ private List imagesToDto(List images) { .build()) .toList(); } - - private CustomSliceResponse mapToCustomSliceResponse(Slice result) { - List content = result.getContent(); - - return CustomSliceResponse.builder() - .content(content) - .pageSize(result.getSize()) - .pageNumber(result.getNumber()) - .hasNext(result.hasNext()) - .build(); - } } diff --git a/module-presentation/src/test/java/com/depromeet/memory/mock/FakeMemoryRepository.java b/module-presentation/src/test/java/com/depromeet/memory/mock/FakeMemoryRepository.java index 9210cd1d..44e3ed96 100644 --- a/module-presentation/src/test/java/com/depromeet/memory/mock/FakeMemoryRepository.java +++ b/module-presentation/src/test/java/com/depromeet/memory/mock/FakeMemoryRepository.java @@ -1,13 +1,12 @@ package com.depromeet.memory.mock; import com.depromeet.memory.Memory; +import com.depromeet.memory.Timeline; import com.depromeet.memory.repository.MemoryRepository; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; public class FakeMemoryRepository implements MemoryRepository { private Long autoGeneratedId = 0L; @@ -99,14 +98,14 @@ public Optional update(Long memoryId, Memory memoryUpdate) { } @Override - public Slice findPrevMemoryByMemberId( - Long memberId, Long cursorId, Pageable pageable, LocalDate recordAt) { + public Timeline findPrevMemoryByMemberId( + Long memberId, LocalDate cursorRecordAt, LocalDate recordAt) { return null; } @Override - public Slice findNextMemoryByMemberId( - Long memberId, Long cursorId, Pageable pageable, LocalDate recordAt) { + public Timeline findNextMemoryByMemberId( + Long memberId, LocalDate cursorRecordAt, LocalDate recordAt) { return null; } diff --git a/module-presentation/src/test/java/com/depromeet/memory/service/StrokeServiceTest.java b/module-presentation/src/test/java/com/depromeet/memory/service/StrokeServiceTest.java index af7e3dca..a47b7134 100644 --- a/module-presentation/src/test/java/com/depromeet/memory/service/StrokeServiceTest.java +++ b/module-presentation/src/test/java/com/depromeet/memory/service/StrokeServiceTest.java @@ -3,6 +3,7 @@ import com.depromeet.memory.Memory; import com.depromeet.memory.Stroke; import com.depromeet.memory.dto.request.StrokeCreateRequest; +import com.depromeet.memory.mock.*; import com.depromeet.memory.mock.FakeMemoryRepository; import com.depromeet.memory.mock.FakeStrokeRepository; import java.time.LocalDate;