From 67244ca9f7bbd85097a0d7a1efe08600a5fdbcc7 Mon Sep 17 00:00:00 2001 From: Park Yun Chan Date: Mon, 26 Aug 2024 21:51:55 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=AF=B8=EC=85=98=20=EA=B8=B0=EB=A1=9D?= =?UTF-8?q?=20=EC=BA=98=EB=A6=B0=EB=8D=94=20=EC=A4=91=EB=B3=B5=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95=20(#230)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/MissionRecordController.java | 20 ++--------- .../application/MissionRecordService.java | 36 ++++++++++++++----- .../dao/MissionRecordRepository.java | 6 ++++ .../dao/MissionRecordRepositoryImpl.java | 6 +++- .../request/MissionRecordCalendarRequest.java | 10 ++++++ .../stonebed/global/error/ErrorCode.java | 1 + .../application/MissionRecordServiceTest.java | 11 ++++-- 7 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 src/main/java/com/depromeet/stonebed/domain/missionRecord/dto/request/MissionRecordCalendarRequest.java diff --git a/src/main/java/com/depromeet/stonebed/domain/missionRecord/api/MissionRecordController.java b/src/main/java/com/depromeet/stonebed/domain/missionRecord/api/MissionRecordController.java index 2de56efc..b9695c2e 100644 --- a/src/main/java/com/depromeet/stonebed/domain/missionRecord/api/MissionRecordController.java +++ b/src/main/java/com/depromeet/stonebed/domain/missionRecord/api/MissionRecordController.java @@ -2,6 +2,7 @@ import com.depromeet.stonebed.domain.missionRecord.application.MissionRecordService; import com.depromeet.stonebed.domain.missionRecord.dto.request.MissionRecordBoostRequest; +import com.depromeet.stonebed.domain.missionRecord.dto.request.MissionRecordCalendarRequest; import com.depromeet.stonebed.domain.missionRecord.dto.request.MissionRecordSaveRequest; import com.depromeet.stonebed.domain.missionRecord.dto.request.MissionRecordStartRequest; import com.depromeet.stonebed.domain.missionRecord.dto.response.MissionRecordCalendarResponse; @@ -12,8 +13,6 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -57,22 +56,9 @@ public void deleteMissionRecord(@PathVariable Long recordId) { @Operation(summary = "캘린더 형식의 미션 기록 조회", description = "회원의 미션 기록을 페이징하여 조회한다.") @GetMapping("/calendar") public MissionRecordCalendarResponse getMissionRecordsForCalendar( - @Parameter(description = "커서 위치", example = "2024-01-01") - @Valid - @RequestParam(required = false) - String cursor, - @Parameter(description = "페이지 당 항목 수", example = "30") - @Valid - @RequestParam - @NotNull - @Min(1) - int limit, - @Parameter(description = "조회할 memberId", example = "1") - @Valid - @RequestParam(required = false) - Long memberId) { + @Valid MissionRecordCalendarRequest request) { - return missionRecordService.getMissionRecordsForCalendar(cursor, limit, memberId); + return missionRecordService.getMissionRecordsForCalendar(request); } @Operation(summary = "수행한 총 미션 기록 수", description = "회원이 수행한 총 미션 기록 수를 조회한다.") diff --git a/src/main/java/com/depromeet/stonebed/domain/missionRecord/application/MissionRecordService.java b/src/main/java/com/depromeet/stonebed/domain/missionRecord/application/MissionRecordService.java index 979056b1..20f1f2f2 100644 --- a/src/main/java/com/depromeet/stonebed/domain/missionRecord/application/MissionRecordService.java +++ b/src/main/java/com/depromeet/stonebed/domain/missionRecord/application/MissionRecordService.java @@ -12,6 +12,7 @@ import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord; import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecordBoost; import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecordStatus; +import com.depromeet.stonebed.domain.missionRecord.dto.request.MissionRecordCalendarRequest; import com.depromeet.stonebed.domain.missionRecord.dto.response.MissionRecordCalendarDto; import com.depromeet.stonebed.domain.missionRecord.dto.response.MissionRecordCalendarResponse; import com.depromeet.stonebed.domain.missionRecord.dto.response.MissionRecordCompleteTotal; @@ -84,6 +85,18 @@ public void saveMission(Long missionId, String content) { MissionHistory missionHistory = findMissionHistoryByIdAndRaisePet(missionId, mission.getRaisePet()); + LocalDate today = LocalDate.now(); + boolean recordExists = + missionRecordRepository.existsByMemberAndMissionHistoryAndCreatedAtBetween( + member, + missionHistory, + today.atStartOfDay(), + today.plusDays(1).atStartOfDay()); + + if (recordExists) { + throw new CustomException(ErrorCode.DUPLICATE_MISSION_RECORD); + } + MissionRecord missionRecord = missionRecordRepository .findByMemberAndMissionHistory(member, missionHistory) @@ -130,15 +143,15 @@ private MissionHistory findMissionHistoryByIdAndRaisePet(Long missionId, RaisePe @Transactional(readOnly = true) public MissionRecordCalendarResponse getMissionRecordsForCalendar( - String cursor, int limit, Long memberId) { + MissionRecordCalendarRequest request) { Long findMemberId = - Optional.ofNullable(memberId) + Optional.ofNullable(request.memberId()) .orElseGet(() -> memberUtil.getCurrentMember().getId()); - Pageable pageable = createPageable(limit); - List records = getMissionRecords(cursor, findMemberId, pageable); + Pageable pageable = createPageable(request.limit()); + List records = getMissionRecords(request.cursor(), findMemberId, pageable); List calendarData = convertToCalendarDto(records); - String nextCursor = getNextCursor(records); + String nextCursor = getNextCursor(records, request.limit()); return MissionRecordCalendarResponse.from(calendarData, nextCursor); } @@ -167,14 +180,19 @@ private List getMissionRecords(String cursor, Long memberId, Page } } - private String getNextCursor(List records) { + private String getNextCursor(List records, int limit) { + if (records.size() < limit) { + return null; + } + return String.valueOf(getLastRecordId(records)); + } + + private Long getLastRecordId(List records) { if (records.isEmpty()) { return null; } - MissionRecord lastRecord = records.get(records.size() - 1); - LocalDate nextCursorDate = lastRecord.getCreatedAt().toLocalDate().plusDays(1); - return nextCursorDate.format(DATE_FORMATTER); + return records.get(records.size() - 1).getId(); } @Transactional(readOnly = true) diff --git a/src/main/java/com/depromeet/stonebed/domain/missionRecord/dao/MissionRecordRepository.java b/src/main/java/com/depromeet/stonebed/domain/missionRecord/dao/MissionRecordRepository.java index 94ea12e3..393fa062 100644 --- a/src/main/java/com/depromeet/stonebed/domain/missionRecord/dao/MissionRecordRepository.java +++ b/src/main/java/com/depromeet/stonebed/domain/missionRecord/dao/MissionRecordRepository.java @@ -24,6 +24,12 @@ List findAllByCreatedAtBetweenAndStatusNot( List findByIdIn(List ids); + boolean existsByMemberAndMissionHistoryAndCreatedAtBetween( + Member member, + MissionHistory missionHistory, + LocalDateTime startOfDay, + LocalDateTime endOfDay); + @Modifying @Query( "UPDATE MissionRecord mr SET mr.deletedAt = CURRENT_TIMESTAMP WHERE mr.member.id = :memberId") diff --git a/src/main/java/com/depromeet/stonebed/domain/missionRecord/dao/MissionRecordRepositoryImpl.java b/src/main/java/com/depromeet/stonebed/domain/missionRecord/dao/MissionRecordRepositoryImpl.java index 5b08e206..90505901 100644 --- a/src/main/java/com/depromeet/stonebed/domain/missionRecord/dao/MissionRecordRepositoryImpl.java +++ b/src/main/java/com/depromeet/stonebed/domain/missionRecord/dao/MissionRecordRepositoryImpl.java @@ -34,7 +34,7 @@ public List findByMemberIdAndCreatedAtFromWithPagination( Long memberId, LocalDateTime createdAt, Pageable pageable) { return queryFactory .selectFrom(missionRecord) - .where(missionRecord.member.id.eq(memberId).and(createdAtFrom(createdAt))) + .where(isMemberId(memberId).and(createdAtFrom(createdAt)).and(isCompleted())) .orderBy(missionRecord.createdAt.asc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) @@ -62,4 +62,8 @@ private BooleanExpression isMemberId(Long memberId) { private BooleanExpression createdAtFrom(LocalDateTime createdAt) { return missionRecord.createdAt.goe(createdAt); } + + private BooleanExpression isCompleted() { + return missionRecord.status.eq(MissionRecordStatus.COMPLETED); + } } diff --git a/src/main/java/com/depromeet/stonebed/domain/missionRecord/dto/request/MissionRecordCalendarRequest.java b/src/main/java/com/depromeet/stonebed/domain/missionRecord/dto/request/MissionRecordCalendarRequest.java new file mode 100644 index 00000000..7929cd53 --- /dev/null +++ b/src/main/java/com/depromeet/stonebed/domain/missionRecord/dto/request/MissionRecordCalendarRequest.java @@ -0,0 +1,10 @@ +package com.depromeet.stonebed.domain.missionRecord.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public record MissionRecordCalendarRequest( + @Schema(description = "커서 위치", example = "2024-01-01") String cursor, + @Schema(description = "페이지 당 항목 수", example = "30") @NotNull @Min(1) int limit, + @Schema(description = "조회할 memberId", example = "1") Long memberId) {} diff --git a/src/main/java/com/depromeet/stonebed/global/error/ErrorCode.java b/src/main/java/com/depromeet/stonebed/global/error/ErrorCode.java index 1fd5bfe2..53ea682c 100644 --- a/src/main/java/com/depromeet/stonebed/global/error/ErrorCode.java +++ b/src/main/java/com/depromeet/stonebed/global/error/ErrorCode.java @@ -36,6 +36,7 @@ public enum ErrorCode { MISSION_HISTORY_NOT_FOUNT(HttpStatus.NOT_FOUND, "해당 일별 미션 정보를 찾을 수 없습니다."), MISSION_RECORD_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 미션 기록을 찾을 수 없습니다."), NO_AVAILABLE_TODAY_MISSION(HttpStatus.INTERNAL_SERVER_ERROR, "할당 가능한 오늘의 미션이 없습니다."), + DUPLICATE_MISSION_RECORD(HttpStatus.BAD_REQUEST, "오늘 완료한 미션이 존재합니다."), // image IMAGE_KEY_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 이미지를 찾을 수 없습니다."), diff --git a/src/test/java/com/depromeet/stonebed/domain/missionRecord/application/MissionRecordServiceTest.java b/src/test/java/com/depromeet/stonebed/domain/missionRecord/application/MissionRecordServiceTest.java index 19a2cce0..edbbb655 100644 --- a/src/test/java/com/depromeet/stonebed/domain/missionRecord/application/MissionRecordServiceTest.java +++ b/src/test/java/com/depromeet/stonebed/domain/missionRecord/application/MissionRecordServiceTest.java @@ -15,6 +15,7 @@ import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord; import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecordBoost; import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecordStatus; +import com.depromeet.stonebed.domain.missionRecord.dto.request.MissionRecordCalendarRequest; import com.depromeet.stonebed.domain.missionRecord.dto.response.MissionRecordCalendarResponse; import com.depromeet.stonebed.domain.missionRecord.dto.response.MissionRecordCompleteTotal; import com.depromeet.stonebed.domain.missionRecord.dto.response.MissionTabResponse; @@ -132,10 +133,13 @@ class MissionRecordServiceTest extends FixtureMonkeySetUp { String cursor = null; int limit = 5; + MissionRecordCalendarRequest request = + new MissionRecordCalendarRequest(cursor, limit, null); + // when MissionRecordCalendarResponse response = missionRecordService.getMissionRecordsForCalendar( - cursor, limit, null); // Pass null for memberId + request); // Pass null for memberId // then then(response).isNotNull(); @@ -160,9 +164,12 @@ class MissionRecordServiceTest extends FixtureMonkeySetUp { String cursor = null; int limit = 5; + MissionRecordCalendarRequest request = + new MissionRecordCalendarRequest(cursor, limit, member.getId()); + // when MissionRecordCalendarResponse response = - missionRecordService.getMissionRecordsForCalendar(cursor, limit, member.getId()); + missionRecordService.getMissionRecordsForCalendar(request); // then then(response).isNotNull();