From 28d24466627e18395d3915dd1d3fb26110e6c5c0 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Mon, 13 Jan 2025 14:49:44 +0900 Subject: [PATCH 01/25] =?UTF-8?q?refactor:=20RecruitBoard=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20=20-=20RecruitStatus=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EA=B0=92=20=EC=A0=9C=EA=B1=B0,=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=20-=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=A0=9C=EA=B1=B0,=20=EB=B9=84=EC=A7=80=EB=8B=88?= =?UTF-8?q?=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recruitboard/domain/RecruitBoard.java | 59 ++++++++----------- .../request/RecruitBoardCreateRequestDto.java | 6 +- .../service/UpdateRecruitBoardService.java | 2 +- .../service/ApplyVolunteerApplyService.java | 2 +- 4 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/domain/RecruitBoard.java b/src/main/java/com/somemore/domains/recruitboard/domain/RecruitBoard.java index 5e12ff5e6..7214dc75b 100644 --- a/src/main/java/com/somemore/domains/recruitboard/domain/RecruitBoard.java +++ b/src/main/java/com/somemore/domains/recruitboard/domain/RecruitBoard.java @@ -1,19 +1,24 @@ package com.somemore.domains.recruitboard.domain; +import static jakarta.persistence.EnumType.STRING; +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PROTECTED; + import com.somemore.domains.recruitboard.dto.request.RecruitBoardUpdateRequestDto; import com.somemore.global.common.entity.BaseEntity; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.Table; +import java.util.UUID; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import java.time.LocalDateTime; -import java.util.UUID; - -import static jakarta.persistence.EnumType.STRING; -import static jakarta.persistence.GenerationType.IDENTITY; -import static lombok.AccessLevel.PROTECTED; - @Getter @NoArgsConstructor(access = PROTECTED) @Entity @@ -42,19 +47,20 @@ public class RecruitBoard extends BaseEntity { @Enumerated(value = STRING) @Column(name = "recruit_status", nullable = false, length = 20) - private RecruitStatus recruitStatus = RecruitStatus.RECRUITING; + private RecruitStatus recruitStatus; @Column(name = "img_url", nullable = false) private String imgUrl; @Builder public RecruitBoard(UUID centerId, Long locationId, String title, String content, - RecruitmentInfo recruitmentInfo, String imgUrl) { + RecruitmentInfo recruitmentInfo, RecruitStatus status, String imgUrl) { this.centerId = centerId; this.locationId = locationId; this.title = title; this.content = content; this.recruitmentInfo = recruitmentInfo; + this.recruitStatus = status; this.imgUrl = imgUrl; } @@ -73,17 +79,18 @@ public void updateWith(String region) { recruitmentInfo.updateWith(region); } - public void changeRecruitStatus(RecruitStatus newStatus, LocalDateTime currentDateTime) { - validateStatusChange(newStatus); - validateChangeDeadline(currentDateTime); - - this.recruitStatus = newStatus; + public void updateRecruitStatus(RecruitStatus status) { + this.recruitStatus = status; } - public boolean isRecruitOpen() { + public boolean isRecruiting() { return this.recruitStatus == RecruitStatus.RECRUITING; } + public boolean isCompleted() { + return this.recruitStatus == RecruitStatus.COMPLETED; + } + private void updateRecruitmentInfo(RecruitBoardUpdateRequestDto dto) { recruitmentInfo.updateWith( dto.region(), @@ -96,24 +103,4 @@ private void updateRecruitmentInfo(RecruitBoardUpdateRequestDto dto) { ); } - public boolean isCompleted() { - return this.recruitStatus == RecruitStatus.COMPLETED; - } - - private void validateStatusChange(RecruitStatus newStatus) { - if (newStatus.isChangeable()) { - return; - } - throw new IllegalArgumentException("상태는 '모집중' 또는 '마감'으로만 변경할 수 있습니다."); - } - - private void validateChangeDeadline(LocalDateTime currentDateTime) { - LocalDateTime volunteerStartDateTime = recruitmentInfo.getVolunteerStartDateTime(); - LocalDateTime deadline = volunteerStartDateTime.toLocalDate().atStartOfDay(); - - if (!currentDateTime.isBefore(deadline)) { - throw new IllegalStateException("봉사 시작 일시 자정 전까지만 상태를 변경할 수 있습니다."); - } - } - } diff --git a/src/main/java/com/somemore/domains/recruitboard/dto/request/RecruitBoardCreateRequestDto.java b/src/main/java/com/somemore/domains/recruitboard/dto/request/RecruitBoardCreateRequestDto.java index b33d54dd9..9beddc3e9 100644 --- a/src/main/java/com/somemore/domains/recruitboard/dto/request/RecruitBoardCreateRequestDto.java +++ b/src/main/java/com/somemore/domains/recruitboard/dto/request/RecruitBoardCreateRequestDto.java @@ -1,5 +1,7 @@ package com.somemore.domains.recruitboard.dto.request; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; + import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.somemore.domains.location.dto.request.LocationCreateRequestDto; @@ -10,10 +12,9 @@ import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import lombok.Builder; - import java.time.LocalDateTime; import java.util.UUID; +import lombok.Builder; @JsonNaming(SnakeCaseStrategy.class) @Builder @@ -69,6 +70,7 @@ public RecruitBoard toEntity(UUID centerId, Long locationId, String imgUrl) { .content(content) .imgUrl(imgUrl) .recruitmentInfo(recruitmentInfo) + .status(RECRUITING) .build(); } } diff --git a/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java b/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java index fe9967994..68d12d4dd 100644 --- a/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java +++ b/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java @@ -48,7 +48,7 @@ public void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerI RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(id); recruitBoardValidator.validateWriter(recruitBoard, centerId); - recruitBoard.changeRecruitStatus(status, currentDateTime); + recruitBoard.updateRecruitStatus(status); } } diff --git a/src/main/java/com/somemore/domains/volunteerapply/service/ApplyVolunteerApplyService.java b/src/main/java/com/somemore/domains/volunteerapply/service/ApplyVolunteerApplyService.java index 152aa79c6..4a4aa8b30 100644 --- a/src/main/java/com/somemore/domains/volunteerapply/service/ApplyVolunteerApplyService.java +++ b/src/main/java/com/somemore/domains/volunteerapply/service/ApplyVolunteerApplyService.java @@ -45,7 +45,7 @@ public Long apply(VolunteerApplyCreateRequestDto requestDto, UUID volunteerId) { } private void validateCanApply(RecruitBoard board) { - if (board.isRecruitOpen()) { + if (board.isRecruiting()) { return; } throw new BadRequestException(RECRUITMENT_NOT_OPEN); From a660ef23704e40c61883c3a20dd3ee50b8fab787 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Mon, 13 Jan 2025 14:50:07 +0900 Subject: [PATCH 02/25] =?UTF-8?q?test(recruit-board):=20RecruitBoard=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=20-=20RecruitStatus=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EA=B0=92=20=EC=A0=9C=EA=B1=B0,=20=EC=83=9D=EC=84=B1=EC=9E=90?= =?UTF-8?q?=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=20-=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0,=20=EB=B9=84=EC=A7=80=EB=8B=88=EC=8A=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recruitboard/domain/RecruitBoardTest.java | 152 ++++++------------ .../domain/RecruitmentInfoTest.java | 36 +++-- .../RecruitBoardRepositoryImplTest.java | 6 +- .../DeleteRecruitBoardServiceTest.java | 17 +- .../service/RecruitBoardQueryServiceTest.java | 31 ++-- .../UpdateRecruitBoardServiceTest.java | 2 + .../support/fixture/RecruitBoardFixture.java | 18 ++- 7 files changed, 117 insertions(+), 145 deletions(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/domain/RecruitBoardTest.java b/src/test/java/com/somemore/domains/recruitboard/domain/RecruitBoardTest.java index 4c9b0a4f1..509baa9bf 100644 --- a/src/test/java/com/somemore/domains/recruitboard/domain/RecruitBoardTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/domain/RecruitBoardTest.java @@ -1,55 +1,44 @@ package com.somemore.domains.recruitboard.domain; -import com.somemore.domains.recruitboard.dto.request.RecruitBoardUpdateRequestDto; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import java.time.LocalDateTime; -import java.util.UUID; - -import static com.somemore.domains.recruitboard.domain.RecruitStatus.*; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; -import static com.somemore.support.fixture.LocalDateTimeFixture.createCurrentDateTime; +import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; import static com.somemore.support.fixture.LocalDateTimeFixture.createUpdateStartDateTime; -import static com.somemore.support.fixture.RecruitBoardFixture.createRecruitBoard; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -class RecruitBoardTest { +import com.somemore.domains.recruitboard.dto.request.RecruitBoardUpdateRequestDto; +import java.time.LocalDateTime; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; - @DisplayName("봉사 모집글 생성시 모집상태는 모집중이다") - @Test - void createRecruitBoardWithDefaultStatus() { - // given - UUID centerId = UUID.randomUUID(); - RecruitBoard board = createRecruitBoard(centerId); +class RecruitBoardTest { - // when - RecruitStatus recruitStatus = board.getRecruitStatus(); + private UUID centerId; + private RecruitBoard board; - // then - assertThat(board.getCenterId()).isEqualTo(centerId); - assertThat(recruitStatus).isEqualTo(RECRUITING); + @BeforeEach + void setUp() { + centerId = UUID.randomUUID(); + board = createRecruitBoard(centerId); } @DisplayName("봉사 모집글을 업데이트 할 수 있다") @Test void updateRecruitBoard() { // given - UUID centerId = UUID.randomUUID(); - RecruitBoard board = createRecruitBoard(centerId); String imgUrl = "https://image.domain.com/updates"; - LocalDateTime startDateTime = createUpdateStartDateTime(); - LocalDateTime endDateTime = startDateTime.plusHours(2); + LocalDateTime updateStartDateTime = createUpdateStartDateTime(); + LocalDateTime updateEndDateTime = updateStartDateTime.plusHours(2); RecruitBoardUpdateRequestDto dto = RecruitBoardUpdateRequestDto.builder() .title("봉사 모집글 작성 수정") .content("봉사 하실분을 모집합니다. 수정
") .recruitmentCount(10) - .volunteerStartDateTime(startDateTime) - .volunteerEndDateTime(endDateTime) + .volunteerStartDateTime(updateStartDateTime) + .volunteerEndDateTime(updateEndDateTime) .volunteerHours(2) .volunteerCategory(OTHER) .admitted(true).build(); @@ -67,8 +56,6 @@ void updateRecruitBoard() { @Test void updateWithRegion() { // given - UUID centerId = UUID.randomUUID(); - RecruitBoard board = createRecruitBoard(centerId); String updateRegion = "새로운지역"; // when @@ -83,11 +70,8 @@ void updateWithRegion() { @Test void isWriterWithCorrectCenterId() { // given - UUID centerId = UUID.randomUUID(); - RecruitBoard recruitBoard = createRecruitBoard(centerId); - // when - boolean isWriter = recruitBoard.isWriter(centerId); + boolean isWriter = board.isWriter(centerId); // then assertThat(isWriter).isTrue(); @@ -96,79 +80,27 @@ void isWriterWithCorrectCenterId() { @DisplayName("잘못된 기관 식별 값이 주어지면 잘못된 작성자인 확인할 수있다.") @Test void isNotWriterWithWrongCenterId() { - UUID centerId = UUID.randomUUID(); + // given UUID wrongId = UUID.randomUUID(); - RecruitBoard recruitBoard = createRecruitBoard(centerId); // when - boolean isWriter = recruitBoard.isWriter(wrongId); + boolean isWriter = board.isWriter(wrongId); // then assertThat(isWriter).isFalse(); } - @DisplayName("모집글 상태를 모집중에서 모집 마감으로 변경할 수 있다") + @DisplayName("모집글 상태를 업데이트 할 수 있다.") @Test - void changeStatusFromRecruitingToClose() { + void updateRecruitStatus() { // given - UUID centerId = UUID.randomUUID(); - RecruitBoard recruitBoard = createRecruitBoard(centerId); - RecruitStatus newStatus = CLOSED; - LocalDateTime currentDateTime = createCurrentDateTime(); + RecruitStatus updateRecruitStatus = CLOSED; // when - recruitBoard.changeRecruitStatus(newStatus, currentDateTime); + board.updateRecruitStatus(updateRecruitStatus); // then - assertThat(recruitBoard.getRecruitStatus()).isEqualTo(newStatus); - } - - @DisplayName("모집글 상태를 모집마감에서 모집중으로 변경할 수 있다") - @Test - void changeStatusFromCloseToRecruiting() { - // given - UUID centerId = UUID.randomUUID(); - RecruitBoard recruitBoard = createRecruitBoard(centerId); - LocalDateTime currentDateTime = createCurrentDateTime(); - recruitBoard.changeRecruitStatus(CLOSED, currentDateTime); - RecruitStatus newStatus = RECRUITING; - - // when - recruitBoard.changeRecruitStatus(newStatus, currentDateTime); - - // then - assertThat(recruitBoard.getRecruitStatus()).isEqualTo(newStatus); - } - - @DisplayName("모집글 상태는 마감으로 변경할 수 없다") - @Test - void changeStatusWhenInvalidStatus() { - // given - UUID centerId = UUID.randomUUID(); - RecruitBoard recruitBoard = createRecruitBoard(centerId); - LocalDateTime currentDateTime = createCurrentDateTime(); - - // when & then - assertThatThrownBy(() -> recruitBoard.changeRecruitStatus(COMPLETED, currentDateTime)) - .isInstanceOf(IllegalArgumentException.class); - } - - @DisplayName("봉사 시작일 자정 이후 모집 상태를 변경할 경우 에러가 발생한다") - @ParameterizedTest - @ValueSource(longs = {0, 1}) - void changeStatusWhenDeadLineAfter(Long secondsOffset) { - // given - UUID centerId = UUID.randomUUID(); - RecruitBoard recruitBoard = createRecruitBoard(centerId); - LocalDateTime deadLineDateTime = recruitBoard.getRecruitmentInfo() - .getVolunteerStartDateTime().toLocalDate().atStartOfDay(); - LocalDateTime currentDateTime = deadLineDateTime.plusSeconds(secondsOffset); - - // when - // then - assertThatThrownBy( - () -> recruitBoard.changeRecruitStatus(CLOSED, currentDateTime) - ).isInstanceOf(IllegalStateException.class); + assertThat(board.getRecruitStatus()).isEqualTo(updateRecruitStatus); } @@ -176,13 +108,35 @@ void changeStatusWhenDeadLineAfter(Long secondsOffset) { @Test void isRecruitOpen() { // given - RecruitBoard board = createRecruitBoard(); - // when - boolean result = board.isRecruitOpen(); + boolean result = board.isRecruiting(); // then assertThat(result).isTrue(); } + public static RecruitBoard createRecruitBoard(UUID centerId) { + LocalDateTime startDateTime = createStartDateTime(); + LocalDateTime endDateTime = startDateTime.plusHours(1); + + RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() + .region("지역") + .recruitmentCount(1) + .volunteerStartDateTime(startDateTime) + .volunteerEndDateTime(endDateTime) + .volunteerHours(1) + .volunteerCategory(OTHER) + .admitted(true) + .build(); + + return RecruitBoard.builder() + .centerId(centerId) + .locationId(1L) + .title("제목") + .content("내용") + .recruitmentInfo(recruitmentInfo) + .status(RECRUITING) + .imgUrl("이미지 링크") + .build(); + } } diff --git a/src/test/java/com/somemore/domains/recruitboard/domain/RecruitmentInfoTest.java b/src/test/java/com/somemore/domains/recruitboard/domain/RecruitmentInfoTest.java index 62c6b1e0b..c6dd15e0b 100644 --- a/src/test/java/com/somemore/domains/recruitboard/domain/RecruitmentInfoTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/domain/RecruitmentInfoTest.java @@ -1,24 +1,29 @@ package com.somemore.domains.recruitboard.domain; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.time.LocalDateTime; - import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; import static com.somemore.domains.recruitboard.domain.VolunteerCategory.SAFETY_PREVENTION; import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; import static com.somemore.support.fixture.LocalDateTimeFixture.createUpdateStartDateTime; import static org.assertj.core.api.Assertions.assertThat; +import java.time.LocalDateTime; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + class RecruitmentInfoTest { + private RecruitmentInfo recruitmentInfo; + + @BeforeEach + void setUp() { + recruitmentInfo = createRecruitmentInfo(); + } + @DisplayName("봉사 활동 정보를 업데이트 할 수 있다") @Test void updateRecruitmentInfo() { // given - RecruitmentInfo recruitmentInfo = createRecruitmentInfo(); - String region = "서울특별시"; Integer count = 2; VolunteerCategory volunteerCategory = SAFETY_PREVENTION; @@ -28,7 +33,8 @@ void updateRecruitmentInfo() { Boolean admitted = false; // when - recruitmentInfo.updateWith(region, count, volunteerCategory, startDateTime, endDateTime, volunteerHours, admitted); + recruitmentInfo.updateWith(region, count, volunteerCategory, startDateTime, endDateTime, + volunteerHours, admitted); // then assertThat(recruitmentInfo.getRecruitmentCount()).isEqualTo(count); @@ -43,7 +49,6 @@ void updateRecruitmentInfo() { @Test void updateRecruitmentInfoWithRegion() { // given - RecruitmentInfo recruitmentInfo = createRecruitmentInfo(); String updateRegion = "새로운지역"; // when @@ -53,22 +58,19 @@ void updateRecruitmentInfoWithRegion() { assertThat(recruitmentInfo.getRegion()).isEqualTo(updateRegion); } - private static RecruitmentInfo createRecruitmentInfo(LocalDateTime startDateTime, - LocalDateTime endDateTime) { + private static RecruitmentInfo createRecruitmentInfo() { + LocalDateTime startDateTime = createStartDateTime(); + LocalDateTime endDateTime = startDateTime.plusHours(1); + return RecruitmentInfo.builder() .region("경기") .recruitmentCount(1) .volunteerStartDateTime(startDateTime) .volunteerEndDateTime(endDateTime) + .volunteerHours(1) .volunteerCategory(OTHER) .admitted(true) .build(); } - private static RecruitmentInfo createRecruitmentInfo() { - LocalDateTime startDateTime = createStartDateTime(); - LocalDateTime endDateTime = startDateTime.plusHours(1); - return createRecruitmentInfo(startDateTime, endDateTime); - } - } diff --git a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java index e40667454..088b3290b 100644 --- a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java @@ -230,12 +230,12 @@ void findNotCompletedIdsByCenterIds() { recruitBoardRepository.save(recruitingBoard); RecruitBoard deletedClosedBoard = createRecruitBoard(centerId); - deletedClosedBoard.changeRecruitStatus(RecruitStatus.CLOSED, createCurrentDateTime()); + deletedClosedBoard.updateRecruitStatus(RecruitStatus.CLOSED); deletedClosedBoard.markAsDeleted(); recruitBoardRepository.save(deletedClosedBoard); RecruitBoard closedBoard = createRecruitBoard(centerId); - closedBoard.changeRecruitStatus(RecruitStatus.CLOSED, createCurrentDateTime()); + closedBoard.updateRecruitStatus(RecruitStatus.CLOSED); recruitBoardRepository.save(closedBoard); RecruitBoard deletedCompletedRecruitBoard = createCompletedRecruitBoard(); @@ -300,7 +300,7 @@ void findAllWithCenterByStatus() { RecruitStatus status = CLOSED; RecruitBoard recruitBoard = createRecruitBoard(center.getId()); LocalDateTime currentDateTime = createCurrentDateTime(); - recruitBoard.changeRecruitStatus(status, currentDateTime); + recruitBoard.updateRecruitStatus(status); recruitBoardRepository.save(recruitBoard); Pageable pageable = getPageable(); diff --git a/src/test/java/com/somemore/domains/recruitboard/service/DeleteRecruitBoardServiceTest.java b/src/test/java/com/somemore/domains/recruitboard/service/DeleteRecruitBoardServiceTest.java index b53e7c542..45b272ece 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/DeleteRecruitBoardServiceTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/DeleteRecruitBoardServiceTest.java @@ -1,23 +1,23 @@ package com.somemore.domains.recruitboard.service; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; +import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; +import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; +import static org.assertj.core.api.Assertions.assertThat; + import com.somemore.domains.recruitboard.domain.RecruitBoard; import com.somemore.domains.recruitboard.domain.RecruitmentInfo; import com.somemore.domains.recruitboard.repository.RecruitBoardRepository; import com.somemore.support.IntegrationTestSupport; +import java.time.LocalDateTime; +import java.util.Optional; +import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; -import java.util.Optional; -import java.util.UUID; - -import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; -import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; -import static org.assertj.core.api.Assertions.assertThat; - @Transactional class DeleteRecruitBoardServiceTest extends IntegrationTestSupport { @@ -73,6 +73,7 @@ private static RecruitBoard createRecruitBoard() { .content("봉사모집내용") .imgUrl("https://image.domain.com/links") .recruitmentInfo(recruitmentInfo) + .status(RECRUITING) .build(); } } diff --git a/src/test/java/com/somemore/domains/recruitboard/service/RecruitBoardQueryServiceTest.java b/src/test/java/com/somemore/domains/recruitboard/service/RecruitBoardQueryServiceTest.java index 508a169b5..839ee31ea 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/RecruitBoardQueryServiceTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/RecruitBoardQueryServiceTest.java @@ -1,5 +1,14 @@ package com.somemore.domains.recruitboard.service; +import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_CENTER; +import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_RECRUIT_BOARD; +import static com.somemore.support.fixture.CenterFixture.createCenter; +import static com.somemore.support.fixture.LocationFixture.createLocation; +import static com.somemore.support.fixture.RecruitBoardFixture.createCompletedRecruitBoard; +import static com.somemore.support.fixture.RecruitBoardFixture.createRecruitBoard; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.somemore.domains.center.domain.Center; import com.somemore.domains.center.repository.center.CenterRepository; import com.somemore.domains.location.domain.Location; @@ -15,6 +24,8 @@ import com.somemore.domains.recruitboard.repository.RecruitBoardRepository; import com.somemore.global.exception.BadRequestException; import com.somemore.support.IntegrationTestSupport; +import java.util.List; +import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,19 +36,6 @@ import org.springframework.data.domain.Sort; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.UUID; - -import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_CENTER; -import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_RECRUIT_BOARD; -import static com.somemore.support.fixture.CenterFixture.createCenter; -import static com.somemore.support.fixture.LocalDateTimeFixture.createCurrentDateTime; -import static com.somemore.support.fixture.LocationFixture.createLocation; -import static com.somemore.support.fixture.RecruitBoardFixture.createCompletedRecruitBoard; -import static com.somemore.support.fixture.RecruitBoardFixture.createRecruitBoard; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - @Transactional class RecruitBoardQueryServiceTest extends IntegrationTestSupport { @@ -236,12 +234,12 @@ void findNotCompletedIdsByCenterIds() { recruitBoardRepository.save(recruitingBoard); RecruitBoard deletedClosedBoard = createRecruitBoard(centerId); - deletedClosedBoard.changeRecruitStatus(RecruitStatus.CLOSED, createCurrentDateTime()); + deletedClosedBoard.updateRecruitStatus(RecruitStatus.CLOSED); deletedClosedBoard.markAsDeleted(); recruitBoardRepository.save(deletedClosedBoard); RecruitBoard closedBoard = createRecruitBoard(centerId); - closedBoard.changeRecruitStatus(RecruitStatus.CLOSED, createCurrentDateTime()); + closedBoard.updateRecruitStatus(RecruitStatus.CLOSED); recruitBoardRepository.save(closedBoard); RecruitBoard deletedCompletedRecruitBoard = createCompletedRecruitBoard(); @@ -252,7 +250,8 @@ void findNotCompletedIdsByCenterIds() { recruitBoardRepository.save(completedRecruitBoard); // when - List notCompletedBoardIds = recruitBoardQueryService.getNotCompletedIdsByCenterIds(centerId); + List notCompletedBoardIds = recruitBoardQueryService.getNotCompletedIdsByCenterIds( + centerId); // then assertThat(notCompletedBoardIds) diff --git a/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java b/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java index 9265db3ee..9b22a6461 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java @@ -20,6 +20,7 @@ import java.time.LocalDateTime; import java.util.UUID; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; import static com.somemore.domains.recruitboard.domain.VolunteerCategory.ADMINISTRATIVE_SUPPORT; import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; import static com.somemore.support.fixture.LocalDateTimeFixture.*; @@ -163,6 +164,7 @@ private static RecruitBoard createRecruitBoard(UUID centerId, Long locationId, L .content("봉사모집내용") .imgUrl("https://image.domain.com/links") .recruitmentInfo(recruitmentInfo) + .status(RECRUITING) .build(); } diff --git a/src/test/java/com/somemore/support/fixture/RecruitBoardFixture.java b/src/test/java/com/somemore/support/fixture/RecruitBoardFixture.java index f28d81538..f7cbc1f40 100644 --- a/src/test/java/com/somemore/support/fixture/RecruitBoardFixture.java +++ b/src/test/java/com/somemore/support/fixture/RecruitBoardFixture.java @@ -11,6 +11,7 @@ import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED; import static com.somemore.domains.recruitboard.domain.RecruitStatus.COMPLETED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; @@ -27,6 +28,7 @@ public class RecruitBoardFixture { private static final String CONTENT = "봉사모집내용"; private static final String IMG_URL = "https://image.domain.com/links"; private static final VolunteerCategory VOLUNTEER_CATEGORY = OTHER; + private static final RecruitStatus STATUS = RECRUITING; private RecruitBoardFixture() { } @@ -50,6 +52,7 @@ public static RecruitBoard createRecruitBoard() { .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -72,6 +75,7 @@ public static RecruitBoard createRecruitBoard(String title) { .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -94,6 +98,7 @@ public static RecruitBoard createRecruitBoard(String title, UUID centerId, Long .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -116,6 +121,7 @@ public static RecruitBoard createRecruitBoard(String title, UUID centerId) { .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -138,6 +144,7 @@ public static RecruitBoard createRecruitBoard(VolunteerCategory category, UUID c .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -160,6 +167,7 @@ public static RecruitBoard createRecruitBoard(Boolean admitted, UUID centerId) { .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -182,6 +190,7 @@ public static RecruitBoard createRecruitBoard(Long locationId) { .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -204,6 +213,7 @@ public static RecruitBoard createRecruitBoard(UUID centerId) { .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(RECRUITING) .build(); } @@ -226,11 +236,11 @@ public static RecruitBoard createRecruitBoard(UUID centerId, Long locationId) { .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } - public static RecruitBoard createRecruitBoard(String region, - VolunteerCategory volunteerCategory) { + public static RecruitBoard createRecruitBoard(String region, VolunteerCategory volunteerCategory) { RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() .region(region) @@ -249,6 +259,7 @@ public static RecruitBoard createRecruitBoard(String region, .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -271,6 +282,7 @@ public static RecruitBoard createRecruitBoard(Long locationId, String title) { .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -293,6 +305,7 @@ public static RecruitBoard createRecruitBoard(LocalDateTime start, LocalDateTime .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); } @@ -315,6 +328,7 @@ public static RecruitBoard createCompletedRecruitBoard(UUID centerId, .content(CONTENT) .imgUrl(IMG_URL) .recruitmentInfo(recruitmentInfo) + .status(STATUS) .build(); setRecruitStatus(recruitBoard, COMPLETED); From 48a7ce148c199099083eba92d47d4e645dd50a7c Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Mon, 13 Jan 2025 15:28:04 +0900 Subject: [PATCH 03/25] =?UTF-8?q?refactor(recruit-board):=20RecruitBoard?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EA=B2=80=EC=A6=9D=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20=20-=20=EB=B4=89=EC=82=AC=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91/=EC=A2=85=EB=A3=8C=20=EC=9D=BC=EC=8B=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=8B=9C,=20=EC=83=9D=EC=84=B1=EC=9D=BC=20=ED=95=98?= =?UTF-8?q?=EB=A3=A8=20=EC=9D=B4=ED=9B=84=EB=B6=80=ED=84=B0=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/RecruitBoardUpdateRequestDto.java | 6 +----- .../service/UpdateRecruitBoardService.java | 3 ++- .../service/validator/RecruitBoardValidator.java | 15 +++++++++++++++ .../global/exception/ExceptionMessage.java | 1 + 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/dto/request/RecruitBoardUpdateRequestDto.java b/src/main/java/com/somemore/domains/recruitboard/dto/request/RecruitBoardUpdateRequestDto.java index 39270be2e..40ebd65f5 100644 --- a/src/main/java/com/somemore/domains/recruitboard/dto/request/RecruitBoardUpdateRequestDto.java +++ b/src/main/java/com/somemore/domains/recruitboard/dto/request/RecruitBoardUpdateRequestDto.java @@ -4,12 +4,10 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.somemore.domains.recruitboard.domain.VolunteerCategory; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import lombok.Builder; - import java.time.LocalDateTime; +import lombok.Builder; @JsonNaming(SnakeCaseStrategy.class) @Builder @@ -28,11 +26,9 @@ public record RecruitBoardUpdateRequestDto( Integer recruitmentCount, @Schema(description = "봉사 시작 일시", example = "2024-12-20T10:00:00", type = "string") @NotNull(message = "봉사 시작 일시는 필수 값입니다.") - @Future(message = "봉사 시작 일시는 내일부터 가능합니다.") LocalDateTime volunteerStartDateTime, @Schema(description = "봉사 종료 일시", example = "2024-12-20T12:00:00", type = "string") @NotNull(message = "봉사 종료 일시는 필수 값입니다.") - @Future(message = "봉사 종료 일시는 내일부터 가능합니다.") LocalDateTime volunteerEndDateTime, @Schema(description = "봉사 시간", example = "2") @NotNull(message = "봉사 시간는 필수 값입니다.") diff --git a/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java b/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java index 68d12d4dd..f63073090 100644 --- a/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java +++ b/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java @@ -28,7 +28,8 @@ public class UpdateRecruitBoardService implements UpdateRecruitBoardUseCase { public void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID centerId, String imgUrl) { RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(id); recruitBoardValidator.validateWriter(recruitBoard, centerId); - recruitBoardValidator.validateRecruitBoardTime(dto.volunteerStartDateTime(), dto.volunteerEndDateTime()); + recruitBoardValidator.validateUpdateRecruitBoardTime(recruitBoard.getCreatedAt(), + dto.volunteerStartDateTime(), dto.volunteerEndDateTime()); recruitBoard.updateWith(dto, imgUrl); } diff --git a/src/main/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidator.java b/src/main/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidator.java index f212a85bb..de80b2def 100644 --- a/src/main/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidator.java +++ b/src/main/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidator.java @@ -2,12 +2,14 @@ import com.somemore.domains.recruitboard.domain.RecruitBoard; import com.somemore.global.exception.BadRequestException; +import java.time.temporal.ChronoUnit; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.UUID; import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME; +import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME_UPDATE; import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD; @Component @@ -21,6 +23,18 @@ public void validateRecruitBoardTime(LocalDateTime startDateTime, LocalDateTime throw new BadRequestException(INVALID_RECRUIT_BOARD_TIME); } + public void validateUpdateRecruitBoardTime(LocalDateTime createdAt, LocalDateTime startDateTime, + LocalDateTime endDateTime) { + validateRecruitBoardTime(startDateTime, endDateTime); + + LocalDateTime oneDayAfterCreatedAt = createdAt.plusDays(1).truncatedTo(ChronoUnit.DAYS); + if (startDateTime.isAfter(oneDayAfterCreatedAt)) { + return; + } + + throw new BadRequestException(INVALID_RECRUIT_BOARD_TIME_UPDATE); + } + public void validateWriter(RecruitBoard recruitBoard, UUID centerId) { if (recruitBoard.isWriter(centerId)) { return; @@ -28,4 +42,5 @@ public void validateWriter(RecruitBoard recruitBoard, UUID centerId) { throw new BadRequestException(UNAUTHORIZED_RECRUIT_BOARD); } + } diff --git a/src/main/java/com/somemore/global/exception/ExceptionMessage.java b/src/main/java/com/somemore/global/exception/ExceptionMessage.java index 8136f9844..94e05f051 100644 --- a/src/main/java/com/somemore/global/exception/ExceptionMessage.java +++ b/src/main/java/com/somemore/global/exception/ExceptionMessage.java @@ -32,6 +32,7 @@ public enum ExceptionMessage { NOT_EXISTS_RECRUIT_BOARD("존재하지 않는 봉사 모집글입니다."), UNAUTHORIZED_RECRUIT_BOARD("해당 봉사 모집글에 권한이 없습니다."), INVALID_RECRUIT_BOARD_TIME("종료 시간은 시작 시간보다 이후여야 합니다."), + INVALID_RECRUIT_BOARD_TIME_UPDATE("변경할수 없는 봉사 시작/종료 일시입니다."), // IMAGE UPLOAD_FAILED("파일 업로드에 실패했습니다."), From 7ea6c6c240cc711d4214538d668e2b48f5457737 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Mon, 13 Jan 2025 15:28:13 +0900 Subject: [PATCH 04/25] =?UTF-8?q?test(recruit-board):=20RecruitBoard=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B2=80=EC=A6=9D=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=20-=20?= =?UTF-8?q?=EB=B4=89=EC=82=AC=20=EC=8B=9C=EC=9E=91/=EC=A2=85=EB=A3=8C=20?= =?UTF-8?q?=EC=9D=BC=EC=8B=9C=20=EC=88=98=EC=A0=95=EC=8B=9C,=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9D=BC=20=ED=95=98=EB=A3=A8=20=EC=9D=B4=ED=9B=84?= =?UTF-8?q?=EB=B6=80=ED=84=B0=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../validator/RecruitBoardValidatorTest.java | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java b/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java index f684b79ab..896941cc5 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java @@ -1,21 +1,21 @@ package com.somemore.domains.recruitboard.service.validator; +import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME; +import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME_UPDATE; +import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD; +import static com.somemore.support.fixture.RecruitBoardFixture.createRecruitBoard; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + import com.somemore.domains.recruitboard.domain.RecruitBoard; import com.somemore.global.exception.BadRequestException; +import java.time.LocalDateTime; +import java.util.UUID; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.time.LocalDateTime; -import java.util.UUID; - -import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME; -import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD; -import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; -import static com.somemore.support.fixture.RecruitBoardFixture.createRecruitBoard; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - class RecruitBoardValidatorTest { private final RecruitBoardValidator validator = new RecruitBoardValidator(); @@ -23,12 +23,13 @@ class RecruitBoardValidatorTest { @DisplayName("봉사 종료 시간이 시작 시간과 같거나 빠르면, 봉사 모집글 생성 시 에러가 발생한다") @ParameterizedTest @ValueSource(longs = {0, -1}) - void createRecruitBoardWithInValidVolunteerTime(long minutesOffset) { + void validateRecruitBoardTimeWhenNotValid(long minutesOffset) { // given - LocalDateTime now = createStartDateTime(); + LocalDateTime now = LocalDateTime.of(2025, 1, 1, 10, 0); LocalDateTime endDateTime = now.plusMinutes(minutesOffset); - // when & then + // when + // then assertThatThrownBy( () -> validator.validateRecruitBoardTime(now, endDateTime)) .isInstanceOf(BadRequestException.class) @@ -36,6 +37,36 @@ void createRecruitBoardWithInValidVolunteerTime(long minutesOffset) { } + @Test + @DisplayName("StartDateTime이 createdAt + 1일 이후일 경우 예외 없이 통과") + void validateUpdateRecruitBoardTime() { + // given + LocalDateTime createdAt = LocalDateTime.of(2025, 1, 1, 10, 0); // 2025-01-01 10:00:00 + LocalDateTime start = LocalDateTime.of(2025, 1, 2, 10, 0); // 2025-01-02 10:00:00 + LocalDateTime end = LocalDateTime.of(2025, 1, 3, 10, 0); // 2025-01-03 10:00:00 + + // when + // then + assertDoesNotThrow(() -> validator.validateUpdateRecruitBoardTime(createdAt, start, end)); + } + + @Test + @DisplayName("StartDateTime이 createdAt + 1일 이전일 경우 예외 발생") + void validateUpdateRecruitBoardTimeWhenNotValid() { + // given + LocalDateTime createdAt = LocalDateTime.of(2025, 1, 2, 10, 0); // 2025-1-1 10:00:00 + LocalDateTime start = LocalDateTime.of(2025, 1, 2, 12, 0); // 2025-1-2 12:00:00 + LocalDateTime end = LocalDateTime.of(2025, 1, 2, 14, 0); // 2025-1-2 14:00:00 + + // when + // then + assertThatThrownBy( + () -> validator.validateUpdateRecruitBoardTime(createdAt, start, end)) + .isInstanceOf(BadRequestException.class) + .hasMessage(INVALID_RECRUIT_BOARD_TIME_UPDATE.getMessage()); + + } + @DisplayName("모집글 작성자가 아닌 경우 에러가 발생한다") @Test void validateWriter() { From 983cbfff88b86dbc1f020070c434831690ad0834 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Mon, 13 Jan 2025 17:16:00 +0900 Subject: [PATCH 05/25] =?UTF-8?q?refactor(recruit-board):=20RecruitBoard?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EA=B2=80=EC=A6=9D=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20=20-=20=EB=AA=A8=EC=A7=91=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=8B=9C=20=EC=8B=9C=EA=B0=84=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EA=B2=80=EC=A6=9D=20=EC=9D=BC=EA=B4=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=20=20=20-=20=EB=B4=89=EC=82=AC=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=EC=9D=BC=EC=8B=9C=20=EC=A0=84=EB=82=A0?= =?UTF-8?q?=EA=B9=8C=EC=A7=80=20=EC=88=98=EC=A0=95=20=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recruitboard/domain/RecruitBoard.java | 6 +++ .../validator/RecruitBoardValidator.java | 43 +++++++++++++------ .../global/exception/ExceptionMessage.java | 2 + 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/domain/RecruitBoard.java b/src/main/java/com/somemore/domains/recruitboard/domain/RecruitBoard.java index 7214dc75b..370029688 100644 --- a/src/main/java/com/somemore/domains/recruitboard/domain/RecruitBoard.java +++ b/src/main/java/com/somemore/domains/recruitboard/domain/RecruitBoard.java @@ -14,6 +14,7 @@ import jakarta.persistence.Id; import jakarta.persistence.Lob; import jakarta.persistence.Table; +import java.time.LocalDateTime; import java.util.UUID; import lombok.Builder; import lombok.Getter; @@ -91,6 +92,11 @@ public boolean isCompleted() { return this.recruitStatus == RecruitStatus.COMPLETED; } + public boolean isUpdatable(LocalDateTime current) { + LocalDateTime deadline = this.recruitmentInfo.getVolunteerStartDateTime().toLocalDate().atStartOfDay(); + return current.isBefore(deadline); + } + private void updateRecruitmentInfo(RecruitBoardUpdateRequestDto dto) { recruitmentInfo.updateWith( dto.region(), diff --git a/src/main/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidator.java b/src/main/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidator.java index de80b2def..edb8d7451 100644 --- a/src/main/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidator.java +++ b/src/main/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidator.java @@ -1,34 +1,36 @@ package com.somemore.domains.recruitboard.service.validator; +import static com.somemore.global.exception.ExceptionMessage.INVALID_DEADLINE_RECRUIT_BOARD_UPDATE; +import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_STATUS_UPDATE; +import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME; +import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME_UPDATE; +import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD; + import com.somemore.domains.recruitboard.domain.RecruitBoard; +import com.somemore.domains.recruitboard.domain.RecruitStatus; import com.somemore.global.exception.BadRequestException; -import java.time.temporal.ChronoUnit; -import org.springframework.stereotype.Component; - import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.UUID; - -import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME; -import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME_UPDATE; -import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD; +import org.springframework.stereotype.Component; @Component public class RecruitBoardValidator { - public void validateRecruitBoardTime(LocalDateTime startDateTime, LocalDateTime endDateTime) { - if (endDateTime.isAfter(startDateTime)) { + public void validateRecruitBoardTime(LocalDateTime start, LocalDateTime end) { + if (end.isAfter(start)) { return; } throw new BadRequestException(INVALID_RECRUIT_BOARD_TIME); } - public void validateUpdateRecruitBoardTime(LocalDateTime createdAt, LocalDateTime startDateTime, - LocalDateTime endDateTime) { - validateRecruitBoardTime(startDateTime, endDateTime); + public void validateUpdateRecruitBoardTime(LocalDateTime createdAt, LocalDateTime start, + LocalDateTime end) { + validateRecruitBoardTime(start, end); LocalDateTime oneDayAfterCreatedAt = createdAt.plusDays(1).truncatedTo(ChronoUnit.DAYS); - if (startDateTime.isAfter(oneDayAfterCreatedAt)) { + if (start.isAfter(oneDayAfterCreatedAt)) { return; } @@ -43,4 +45,19 @@ public void validateWriter(RecruitBoard recruitBoard, UUID centerId) { throw new BadRequestException(UNAUTHORIZED_RECRUIT_BOARD); } + public void validateUpdatable(RecruitBoard recruitBoard, LocalDateTime current) { + if (recruitBoard.isUpdatable(current)) { + return; + } + + throw new BadRequestException(INVALID_DEADLINE_RECRUIT_BOARD_UPDATE); + } + + public void validateRecruitStatus(RecruitStatus newStatus) { + if (newStatus.isChangeable()) { + return; + } + throw new BadRequestException(INVALID_RECRUIT_BOARD_STATUS_UPDATE); + } + } diff --git a/src/main/java/com/somemore/global/exception/ExceptionMessage.java b/src/main/java/com/somemore/global/exception/ExceptionMessage.java index 94e05f051..746924eb9 100644 --- a/src/main/java/com/somemore/global/exception/ExceptionMessage.java +++ b/src/main/java/com/somemore/global/exception/ExceptionMessage.java @@ -33,6 +33,8 @@ public enum ExceptionMessage { UNAUTHORIZED_RECRUIT_BOARD("해당 봉사 모집글에 권한이 없습니다."), INVALID_RECRUIT_BOARD_TIME("종료 시간은 시작 시간보다 이후여야 합니다."), INVALID_RECRUIT_BOARD_TIME_UPDATE("변경할수 없는 봉사 시작/종료 일시입니다."), + INVALID_RECRUIT_BOARD_STATUS_UPDATE("상태는 '모집중' 또는 '마감'으로만 변경할 수 있습니다."), + INVALID_DEADLINE_RECRUIT_BOARD_UPDATE("봉사 시작 일시 자정 전까지 변경할 수 있습니다."), // IMAGE UPLOAD_FAILED("파일 업로드에 실패했습니다."), From 0a50031f1ae5e166a24180da6051409a46075753 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Mon, 13 Jan 2025 17:16:13 +0900 Subject: [PATCH 06/25] =?UTF-8?q?test(recruit-board):=20RecruitBoard=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B2=80=EC=A6=9D=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=20-=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=88=98=EC=A0=95=EC=8B=9C=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=9D=BC=EA=B4=84=20=EC=B6=94=EA=B0=80=20=20=20=20?= =?UTF-8?q?-=20=EB=B4=89=EC=82=AC=20=EC=8B=9C=EC=9E=91=20=EC=9D=BC?= =?UTF-8?q?=EC=8B=9C=20=EC=A0=84=EB=82=A0=EA=B9=8C=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recruitboard/domain/RecruitBoardTest.java | 16 ++++ .../validator/RecruitBoardValidatorTest.java | 93 ++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/domain/RecruitBoardTest.java b/src/test/java/com/somemore/domains/recruitboard/domain/RecruitBoardTest.java index 509baa9bf..2ef35a2e2 100644 --- a/src/test/java/com/somemore/domains/recruitboard/domain/RecruitBoardTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/domain/RecruitBoardTest.java @@ -3,6 +3,7 @@ import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED; import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; +import static com.somemore.support.fixture.LocalDateTimeFixture.createCurrentDateTime; import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; import static com.somemore.support.fixture.LocalDateTimeFixture.createUpdateStartDateTime; import static org.assertj.core.api.Assertions.assertThat; @@ -115,6 +116,21 @@ void isRecruitOpen() { assertThat(result).isTrue(); } + @DisplayName("모집글 수정 가능 여부를 확인할 수 있다.") + @Test + void isUpdatable() { + // given + // volunteerStartDateTime 내일 13:00:00 + // current 오늘 16:00:00 + LocalDateTime current = createCurrentDateTime(); + + // when + boolean updatable = board.isUpdatable(current); + + // then + assertThat(updatable).isTrue(); + } + public static RecruitBoard createRecruitBoard(UUID centerId) { LocalDateTime startDateTime = createStartDateTime(); LocalDateTime endDateTime = startDateTime.plusHours(1); diff --git a/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java b/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java index 896941cc5..1eb830958 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java @@ -1,16 +1,27 @@ package com.somemore.domains.recruitboard.service.validator; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.COMPLETED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; +import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; +import static com.somemore.global.exception.ExceptionMessage.INVALID_DEADLINE_RECRUIT_BOARD_UPDATE; import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME; import static com.somemore.global.exception.ExceptionMessage.INVALID_RECRUIT_BOARD_TIME_UPDATE; import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD; +import static com.somemore.support.fixture.LocalDateTimeFixture.createCurrentDateTime; +import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; import static com.somemore.support.fixture.RecruitBoardFixture.createRecruitBoard; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import com.somemore.domains.recruitboard.domain.RecruitBoard; +import com.somemore.domains.recruitboard.domain.RecruitStatus; +import com.somemore.domains.recruitboard.domain.RecruitmentInfo; import com.somemore.global.exception.BadRequestException; +import com.somemore.global.exception.ExceptionMessage; import java.time.LocalDateTime; import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -20,6 +31,12 @@ class RecruitBoardValidatorTest { private final RecruitBoardValidator validator = new RecruitBoardValidator(); + private RecruitBoard board; + @BeforeEach + void setUp() { + board = createRecruitBoard(); + } + @DisplayName("봉사 종료 시간이 시작 시간과 같거나 빠르면, 봉사 모집글 생성 시 에러가 발생한다") @ParameterizedTest @ValueSource(longs = {0, -1}) @@ -72,7 +89,7 @@ void validateUpdateRecruitBoardTimeWhenNotValid() { void validateWriter() { // given UUID wrongCenterId = UUID.randomUUID(); - RecruitBoard board = createRecruitBoard(UUID.randomUUID()); + RecruitBoard board = createRecruitBoard(); // when // then @@ -82,4 +99,78 @@ void validateWriter() { .hasMessage(UNAUTHORIZED_RECRUIT_BOARD.getMessage()); } + @DisplayName("모집글 업데이트는 봉사 시작 일시 전날까지 가능하다") + @Test + void validateUpdatable() { + // given + LocalDateTime current = createCurrentDateTime(); + + // when + // then + assertDoesNotThrow(() -> validator.validateUpdatable(board, current)); + } + + @DisplayName("봉사 시작 일시 00시 이후 모집글 업데이트시 에러가 발생한다.") + @Test + void validateUpdatableWhenNotValid() { + // given + LocalDateTime wrongDateTime = board.getRecruitmentInfo().getVolunteerStartDateTime(); + + // when + // then + assertThatThrownBy( + () -> validator.validateUpdatable(board, wrongDateTime)) + .isInstanceOf(BadRequestException.class) + .hasMessage(INVALID_DEADLINE_RECRUIT_BOARD_UPDATE.getMessage()); + } + + @DisplayName("모집글 상태는 모집중, 마감으로 변경할 수 있다.") + @Test + void validateRecruitStatus() { + // given + RecruitStatus status = CLOSED; + + // when & then + assertDoesNotThrow(() -> validator.validateRecruitStatus(status)); + } + + @DisplayName("모집글 상태를 종료변경하는 경우 에러가 발생한다.") + @Test + void validateRecruitStatusWhenNotValid() { + // given + RecruitStatus status = COMPLETED; + + // when + // then + assertThatThrownBy( + () -> validator.validateRecruitStatus(status)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ExceptionMessage.INVALID_RECRUIT_BOARD_STATUS_UPDATE.getMessage()); + } + + public static RecruitBoard createRecruitBoard() { + LocalDateTime startDateTime = createStartDateTime(); + LocalDateTime endDateTime = startDateTime.plusHours(1); + + RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() + .region("지역") + .recruitmentCount(1) + .volunteerStartDateTime(startDateTime) + .volunteerEndDateTime(endDateTime) + .volunteerHours(1) + .volunteerCategory(OTHER) + .admitted(true) + .build(); + + return RecruitBoard.builder() + .centerId(UUID.randomUUID()) + .locationId(1L) + .title("제목") + .content("내용") + .recruitmentInfo(recruitmentInfo) + .status(RECRUITING) + .imgUrl("이미지 링크") + .build(); + } + } From 94077aa84d7b4669065f17bf826faf8ddba48135 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Mon, 13 Jan 2025 17:16:33 +0900 Subject: [PATCH 07/25] =?UTF-8?q?refactor(recruit-board):=20RecruitBoard?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EA=B2=80=EC=A6=9D=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecruitBoardCommandApiController.java | 7 ++-- .../service/UpdateRecruitBoardService.java | 42 ++++++++++++------- .../usecase/UpdateRecruitBoardUseCase.java | 12 ++++-- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiController.java b/src/main/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiController.java index 7f4f401d9..4f236d8c0 100644 --- a/src/main/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiController.java +++ b/src/main/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiController.java @@ -63,7 +63,8 @@ public ApiResponse updateRecruitBoard( @RequestPart(value = "img_file", required = false) MultipartFile image ) { String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image)); - updateRecruitBoardUseCase.updateRecruitBoard(requestDto, id, userId, imgUrl); + LocalDateTime now = LocalDateTime.now(); + updateRecruitBoardUseCase.updateRecruitBoard(requestDto, id, userId, imgUrl, now); return ApiResponse.ok("봉사 활동 모집글 수정 성공"); } @@ -76,8 +77,8 @@ public ApiResponse updateRecruitBoardLocation( @PathVariable Long id, @Valid @RequestBody RecruitBoardLocationUpdateRequestDto requestDto ) { - - updateRecruitBoardUseCase.updateRecruitBoardLocation(requestDto, id, userId); + LocalDateTime now = LocalDateTime.now(); + updateRecruitBoardUseCase.updateRecruitBoardLocation(requestDto, id, userId, now); return ApiResponse.ok("봉사 활동 모집글 위치 수정 성공"); } diff --git a/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java b/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java index f63073090..d9215ebe1 100644 --- a/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java +++ b/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java @@ -8,13 +8,12 @@ import com.somemore.domains.recruitboard.service.validator.RecruitBoardValidator; import com.somemore.domains.recruitboard.usecase.RecruitBoardQueryUseCase; import com.somemore.domains.recruitboard.usecase.UpdateRecruitBoardUseCase; +import java.time.LocalDateTime; +import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; -import java.util.UUID; - @RequiredArgsConstructor @Transactional @Service @@ -25,9 +24,11 @@ public class UpdateRecruitBoardService implements UpdateRecruitBoardUseCase { private final RecruitBoardValidator recruitBoardValidator; @Override - public void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID centerId, String imgUrl) { - RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(id); - recruitBoardValidator.validateWriter(recruitBoard, centerId); + public void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID centerId, + String imgUrl, LocalDateTime current) { + RecruitBoard recruitBoard = getRecruitBoard(id); + validateUpdatableAndWriter(recruitBoard, centerId, current); + recruitBoardValidator.validateUpdateRecruitBoardTime(recruitBoard.getCreatedAt(), dto.volunteerStartDateTime(), dto.volunteerEndDateTime()); @@ -35,21 +36,34 @@ public void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID c } @Override - public void updateRecruitBoardLocation(RecruitBoardLocationUpdateRequestDto requestDto, Long id, UUID centerId) { - RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(id); - recruitBoardValidator.validateWriter(recruitBoard, centerId); - - updateLocationUseCase.updateLocation(requestDto.toLocationUpdateRequestDto(), recruitBoard.getLocationId()); + public void updateRecruitBoardLocation(RecruitBoardLocationUpdateRequestDto requestDto, Long id, + UUID centerId, LocalDateTime current) { + RecruitBoard recruitBoard = getRecruitBoard(id); + validateUpdatableAndWriter(recruitBoard, centerId, current); + updateLocationUseCase.updateLocation(requestDto.toLocationUpdateRequestDto(), + recruitBoard.getLocationId()); recruitBoard.updateWith(requestDto.region()); } @Override - public void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerId, LocalDateTime currentDateTime) { - RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(id); - recruitBoardValidator.validateWriter(recruitBoard, centerId); + public void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerId, + LocalDateTime current) { + RecruitBoard recruitBoard = getRecruitBoard(id); + validateUpdatableAndWriter(recruitBoard, centerId, current); + recruitBoardValidator.validateRecruitStatus(status); recruitBoard.updateRecruitStatus(status); } + private void validateUpdatableAndWriter(RecruitBoard recruitBoard, UUID centerId, + LocalDateTime current) { + recruitBoardValidator.validateUpdatable(recruitBoard, current); + recruitBoardValidator.validateWriter(recruitBoard, centerId); + } + + private RecruitBoard getRecruitBoard(Long id) { + return recruitBoardQueryUseCase.getById(id); + } } + diff --git a/src/main/java/com/somemore/domains/recruitboard/usecase/UpdateRecruitBoardUseCase.java b/src/main/java/com/somemore/domains/recruitboard/usecase/UpdateRecruitBoardUseCase.java index 3403d7711..4cb318fe6 100644 --- a/src/main/java/com/somemore/domains/recruitboard/usecase/UpdateRecruitBoardUseCase.java +++ b/src/main/java/com/somemore/domains/recruitboard/usecase/UpdateRecruitBoardUseCase.java @@ -3,14 +3,18 @@ import com.somemore.domains.recruitboard.domain.RecruitStatus; import com.somemore.domains.recruitboard.dto.request.RecruitBoardLocationUpdateRequestDto; import com.somemore.domains.recruitboard.dto.request.RecruitBoardUpdateRequestDto; - import java.time.LocalDateTime; import java.util.UUID; public interface UpdateRecruitBoardUseCase { - void updateRecruitBoard(RecruitBoardUpdateRequestDto requestDto, Long id, UUID centerId, String imgUrl); - void updateRecruitBoardLocation(RecruitBoardLocationUpdateRequestDto requestDto, Long id, UUID centerId); + void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID centerId, String imgUrl, + LocalDateTime current); + + + void updateRecruitBoardLocation(RecruitBoardLocationUpdateRequestDto requestDto, Long id, + UUID centerId, LocalDateTime current); - void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerId, LocalDateTime currentDateTime); + void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerId, + LocalDateTime currentDateTime); } From 55c40ab11fc8384f24643007f16c9572d845826c Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Mon, 13 Jan 2025 17:16:42 +0900 Subject: [PATCH 08/25] =?UTF-8?q?test(recruit-board):=20RecruitBoard=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B2=80=EC=A6=9D=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RecruitBoardCommandApiControllerTest.java | 4 ++-- .../recruitboard/service/UpdateRecruitBoardServiceTest.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiControllerTest.java b/src/test/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiControllerTest.java index 2384efc5d..6c1e2e811 100644 --- a/src/test/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiControllerTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiControllerTest.java @@ -150,7 +150,7 @@ void updateRecruitBoard() throws Exception { given(imageUploadUseCase.uploadImage(any())).willReturn(mockImageUrl); willDoNothing().given(updateRecruitBoardUseCase) - .updateRecruitBoard(any(), any(), any(UUID.class), anyString()); + .updateRecruitBoard(any(), any(), any(UUID.class), anyString(), any()); MockMultipartHttpServletRequestBuilder builder = multipart("/api/recruit-board/{id}", 1); builder.with(new RequestPostProcessor() { @@ -187,7 +187,7 @@ void updateRecruitBoardLocation() throws Exception { .build(); willDoNothing().given(updateRecruitBoardUseCase) - .updateRecruitBoardLocation(any(), any(), any(UUID.class)); + .updateRecruitBoardLocation(any(), any(), any(UUID.class), any()); String requestBody = objectMapper.writeValueAsString(requestDto); diff --git a/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java b/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java index 9b22a6461..fbe50f4ab 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java @@ -57,6 +57,7 @@ void setUp() { @Test void updateRecruitBoard() { // given + LocalDateTime current = createCurrentDateTime(); LocalDateTime newStartDateTime = createUpdateStartDateTime(); LocalDateTime newEndDateTime = newStartDateTime.plusHours(3); String newImgUrl = "https://image.domain.com/updates"; @@ -73,7 +74,7 @@ void updateRecruitBoard() { .build(); // when - updateRecruitBoardService.updateRecruitBoard(dto, recruitBoard.getId(), centerId, newImgUrl); + updateRecruitBoardService.updateRecruitBoard(dto, recruitBoard.getId(), centerId, newImgUrl, current); // then RecruitBoard updatedRecruitBoard = recruitBoardRepository.findById(recruitBoard.getId()) @@ -98,6 +99,7 @@ void updateRecruitBoard() { @Test void updateRecruitBoardLocation() { // given + LocalDateTime current = createCurrentDateTime(); RecruitBoardLocationUpdateRequestDto dto = RecruitBoardLocationUpdateRequestDto.builder() .region("새로새로지역지역") .address("새로새로주소주소") @@ -106,7 +108,7 @@ void updateRecruitBoardLocation() { .build(); // when - updateRecruitBoardService.updateRecruitBoardLocation(dto, recruitBoard.getId(), centerId); + updateRecruitBoardService.updateRecruitBoardLocation(dto, recruitBoard.getId(), centerId, current); // then RecruitBoard updateRecruitBoard = recruitBoardRepository.findById(recruitBoard.getId()) From 9a2cc8a0a7e257f3985e8ed14eea1781059897fd Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 14:23:03 +0900 Subject: [PATCH 09/25] =?UTF-8?q?chore:=20retry=20=EC=A2=85=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index e765995fd..b9a857960 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,7 @@ dependencies { compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.6.0' + implementation("org.springframework.retry:spring-retry") // Monitoring implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '3.3.3' From 0545ba603b6ede85061926b4433426a69c4b5e50 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 14:24:00 +0900 Subject: [PATCH 10/25] =?UTF-8?q?feat(recruit-board):=20=EB=AA=A8=EC=A7=91?= =?UTF-8?q?=EA=B8=80=20=EC=83=81=ED=83=9C=20=EC=8A=A4=EC=BC=80=EC=A5=B4?= =?UTF-8?q?=EB=A7=81=20=20-=20=EB=B4=89=EC=82=AC=20=EC=8B=9C=EC=9E=91?= =?UTF-8?q?=EC=9D=BC=20=EA=B8=B0=EC=A4=80=20=EB=AA=A8=EC=A7=91=EC=99=84?= =?UTF-8?q?=EB=A3=8C(CLOSED)=20=EB=B3=80=EA=B2=BD=20=20-=20=EB=B4=89?= =?UTF-8?q?=EC=82=AC=20=EC=A2=85=EB=A3=8C=EC=9D=BC=20=EA=B8=B0=EC=A4=80=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C(COMPLETED)=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/RecruitBoardRepository.java | 15 +- .../RecruitBoardRepositoryImpl.java | 170 +++++++++++------- .../RecruitBoardStatusUpdateScheduler.java | 52 ++++++ 3 files changed, 164 insertions(+), 73 deletions(-) create mode 100644 src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java diff --git a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java index 485640563..68a5e3934 100644 --- a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java +++ b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java @@ -1,12 +1,12 @@ package com.somemore.domains.recruitboard.repository; +import com.somemore.domains.recruitboard.domain.RecruitBoard; import com.somemore.domains.recruitboard.dto.condition.RecruitBoardNearByCondition; import com.somemore.domains.recruitboard.dto.condition.RecruitBoardSearchCondition; import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardDetail; import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithCenter; import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithLocation; -import com.somemore.domains.recruitboard.domain.RecruitBoard; - +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -26,17 +26,20 @@ public interface RecruitBoardRepository { Page findAllNearby(RecruitBoardNearByCondition condition); -// Page findAllNearbyWithKeyword(RecruitBoardNearByCondition condition); - - Page findAllByCenterId(UUID centerId, RecruitBoardSearchCondition condition); List findNotCompletedIdsByCenterId(UUID centerId); List findAllByIds(List ids); + List findAll(); + + long updateRecruitingToClosedByStartDate(LocalDateTime startTime, LocalDateTime endTime); + + long updateClosedToCompletedByEndDate(LocalDateTime now); + +// Page findAllNearbyWithKeyword(RecruitBoardNearByCondition condition); // Page findByRecruitBoardsContaining(RecruitBoardSearchCondition condition); // void saveDocuments(List recruitBoards); - List findAll(); // void deleteDocument(Long id); } diff --git a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java index 33975eaf0..83de26e6e 100644 --- a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java +++ b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java @@ -1,5 +1,9 @@ package com.somemore.domains.recruitboard.repository; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.COMPLETED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; + import com.querydsl.core.types.ConstructorExpression; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Projections; @@ -8,17 +12,17 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import com.somemore.domains.center.domain.QCenter; import com.somemore.domains.location.domain.QLocation; +import com.somemore.domains.location.utils.GeoUtils; import com.somemore.domains.recruitboard.domain.QRecruitBoard; +import com.somemore.domains.recruitboard.domain.RecruitBoard; +import com.somemore.domains.recruitboard.domain.RecruitStatus; +import com.somemore.domains.recruitboard.domain.VolunteerCategory; import com.somemore.domains.recruitboard.dto.condition.RecruitBoardNearByCondition; import com.somemore.domains.recruitboard.dto.condition.RecruitBoardSearchCondition; import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardDetail; import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithCenter; import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithLocation; -import com.somemore.domains.location.utils.GeoUtils; -import com.somemore.domains.recruitboard.domain.RecruitBoard; -import com.somemore.domains.recruitboard.domain.RecruitStatus; -import com.somemore.domains.recruitboard.domain.VolunteerCategory; - +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -35,8 +39,8 @@ public class RecruitBoardRepositoryImpl implements RecruitBoardRepository { private final RecruitBoardJpaRepository recruitBoardJpaRepository; - // private final RecruitBoardDocumentRepository documentRepository; private final JPAQueryFactory queryFactory; + // private final RecruitBoardDocumentRepository documentRepository; private static final QRecruitBoard recruitBoard = QRecruitBoard.recruitBoard; private static final QLocation location = QLocation.location; @@ -66,31 +70,6 @@ public Optional findById(Long id) { return Optional.ofNullable(result); } - @Override - public List findNotCompletedIdsByCenterId(UUID centerId) { - - BooleanExpression exp = centerIdEq(centerId) - .and(isNotCompleted()) - .and(isNotDeleted()); - - return queryFactory - .select(recruitBoard.id) - .from(recruitBoard) - .where(exp) - .fetch(); - } - - @Override - public List findAllByIds(List ids) { - BooleanExpression exp = recruitBoard.id.in(ids) - .and(isNotDeleted()); - - return queryFactory - .selectFrom(recruitBoard) - .where(exp) - .fetch(); - } - @Override public Optional findWithLocationById(Long id) { @@ -170,6 +149,88 @@ public Page findAllNearby(RecruitBoardNearByCondition condit return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); } + @Override + public Page findAllByCenterId(UUID centerId, + RecruitBoardSearchCondition condition) { + + BooleanExpression exp = centerIdEq(centerId) + .and(keywordEq(condition.keyword())) + .and(volunteerCategoryEq(condition.category())) + .and(regionEq(condition.region())) + .and(admittedEq(condition.admitted())) + .and(statusEq(condition.status())) + .and(isNotDeleted()); + + Pageable pageable = condition.pageable(); + + List content = queryFactory + .selectFrom(recruitBoard) + .where(exp) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .orderBy(toOrderSpecifiers(pageable.getSort())) + .fetch(); + + JPAQuery countQuery = queryFactory + .select(recruitBoard.count()) + .from(recruitBoard) + .where(exp); + + return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); + } + + @Override + public List findNotCompletedIdsByCenterId(UUID centerId) { + + BooleanExpression exp = centerIdEq(centerId) + .and(isNotCompleted()) + .and(isNotDeleted()); + + return queryFactory + .select(recruitBoard.id) + .from(recruitBoard) + .where(exp) + .fetch(); + } + + @Override + public List findAllByIds(List ids) { + BooleanExpression exp = recruitBoard.id.in(ids) + .and(isNotDeleted()); + + return queryFactory + .selectFrom(recruitBoard) + .where(exp) + .fetch(); + } + + @Override + public List findAll() { + return recruitBoardJpaRepository.findAll(); + } + + @Override + public long updateRecruitingToClosedByStartDate(LocalDateTime startTime, LocalDateTime endTime) { + BooleanExpression exp = statusEq(RECRUITING) + .and(volunteerStartDateTimeBetween(startTime, endTime)); + + return queryFactory.update(recruitBoard) + .set(recruitBoard.recruitStatus, CLOSED) + .where(exp) + .execute(); + } + + @Override + public long updateClosedToCompletedByEndDate(LocalDateTime now) { + BooleanExpression exp = statusEq(CLOSED) + .and(volunteerEndDateTimeBefore(now)); + + return queryFactory.update(recruitBoard) + .set(recruitBoard.recruitStatus, COMPLETED) + .where(exp) + .execute(); + } + // @Override // public Page findAllNearbyWithKeyword(RecruitBoardNearByCondition condition) { // QRecruitBoard recruitBoard = QRecruitBoard.recruitBoard; @@ -209,37 +270,8 @@ public Page findAllNearby(RecruitBoardNearByCondition condit // .and(predicate)); // // return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); -// } - @Override - public Page findAllByCenterId(UUID centerId, - RecruitBoardSearchCondition condition) { - - BooleanExpression exp = centerIdEq(centerId) - .and(keywordEq(condition.keyword())) - .and(volunteerCategoryEq(condition.category())) - .and(regionEq(condition.region())) - .and(admittedEq(condition.admitted())) - .and(statusEq(condition.status())) - .and(isNotDeleted()); - - Pageable pageable = condition.pageable(); - - List content = queryFactory - .selectFrom(recruitBoard) - .where(exp) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) - .orderBy(toOrderSpecifiers(pageable.getSort())) - .fetch(); - - JPAQuery countQuery = queryFactory - .select(recruitBoard.count()) - .from(recruitBoard) - .where(exp); - - return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne); - } +// } // @Override // public Page findByRecruitBoardsContaining(RecruitBoardSearchCondition condition) { @@ -287,11 +319,6 @@ public Page findAllByCenterId(UUID centerId, // documentRepository.saveAll(recruitBoardDocuments); // } - @Override - public List findAll() { - return recruitBoardJpaRepository.findAll(); - } - // @Override // public void deleteDocument(Long id) { // documentRepository.deleteById(id); @@ -310,7 +337,7 @@ private BooleanExpression isNotDeleted() { } private BooleanExpression isNotCompleted() { - return recruitBoard.recruitStatus.in(RecruitStatus.RECRUITING, RecruitStatus.CLOSED); + return recruitBoard.recruitStatus.in(RECRUITING, CLOSED); } private BooleanExpression keywordEq(String keyword) { @@ -354,6 +381,15 @@ private BooleanExpression locationBetween(RecruitBoardNearByCondition condition) .and(location.longitude.between(minLongitude, maxLongitude)); } + private static BooleanExpression volunteerStartDateTimeBetween(LocalDateTime startTime, + LocalDateTime endTime) { + return recruitBoard.recruitmentInfo.volunteerStartDateTime.between(startTime, endTime); + } + + private static BooleanExpression volunteerEndDateTimeBefore(LocalDateTime now) { + return recruitBoard.recruitmentInfo.volunteerEndDateTime.before(now); + } + private OrderSpecifier[] toOrderSpecifiers(Sort sort) { return sort.stream() .map(order -> { diff --git a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java new file mode 100644 index 000000000..41a9cd324 --- /dev/null +++ b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java @@ -0,0 +1,52 @@ +package com.somemore.domains.recruitboard.scheduler; + +import com.somemore.domains.recruitboard.repository.RecruitBoardRepository; +import jakarta.transaction.Transactional; +import java.time.LocalDate; +import java.time.LocalDateTime; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Slf4j +@RequiredArgsConstructor +@Transactional +@Component +public class RecruitBoardStatusUpdateScheduler { + + private final RecruitBoardRepository recruitBoardRepository; + + @Retryable( + retryFor = Exception.class, + backoff = @Backoff(delay = 100000) + ) + @Scheduled(cron = "0 0 0 * * ?") + public void updateRecruitBoardStatusToClosed() { + log.info("봉사 시작일에 해당하는 모집글 상태를 CLOSED로 변경하는 작업 시작"); + LocalDateTime startOfDay = LocalDate.now().atStartOfDay(); + LocalDateTime startOfNextDay = LocalDate.now().plusDays(1).atStartOfDay(); + + long updatedCount = recruitBoardRepository.updateRecruitingToClosedByStartDate(startOfDay, + startOfNextDay); + log.info("총 {}개의 모집글 상태를 CLOSED로 변경 완료", updatedCount); + + } + + @Retryable( + retryFor = Exception.class, + backoff = @Backoff(delay = 100000) + ) + @Scheduled(cron = "0 0 0 * * ?") + public void updateRecruitBoardStatusToCompleted() { + log.info("봉사 종료일이 지난 모집글 상태를 COMPLETED로 변경하는 작업 시작"); + LocalDateTime now = LocalDateTime.now(); + + long updatedCount = recruitBoardRepository.updateClosedToCompletedByEndDate(now); + log.info("총 {}개의 모집글 상태를 COMPLETED로 변경 완료", updatedCount); + + } + +} From 140852a749eaadbd5aa5da27b0f2851affa6c2c6 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 14:24:10 +0900 Subject: [PATCH 11/25] =?UTF-8?q?test(recruit-board):=20=EB=AA=A8=EC=A7=91?= =?UTF-8?q?=EA=B8=80=20=EC=83=81=ED=83=9C=20=EC=8A=A4=EC=BC=80=EC=A5=B4?= =?UTF-8?q?=EB=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=20-=20=EB=B4=89?= =?UTF-8?q?=EC=82=AC=20=EC=8B=9C=EC=9E=91=EC=9D=BC=20=EA=B8=B0=EC=A4=80=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EC=99=84=EB=A3=8C(CLOSED)=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=20-=20=EB=B4=89=EC=82=AC=20=EC=A2=85=EB=A3=8C?= =?UTF-8?q?=EC=9D=BC=20=EA=B8=B0=EC=A4=80=20=EC=99=84=EB=A3=8C(COMPLETED)?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecruitBoardRepositoryImplTest.java | 346 +++++++++++------- ...RecruitBoardStatusUpdateSchedulerTest.java | 106 ++++++ 2 files changed, 323 insertions(+), 129 deletions(-) create mode 100644 src/test/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateSchedulerTest.java diff --git a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java index 088b3290b..2a4f64a86 100644 --- a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java @@ -1,11 +1,21 @@ package com.somemore.domains.recruitboard.repository; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.COMPLETED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; +import static com.somemore.domains.recruitboard.domain.VolunteerCategory.ADMINISTRATIVE_SUPPORT; +import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; +import static com.somemore.support.fixture.CenterFixture.createCenter; +import static com.somemore.support.fixture.LocationFixture.createLocation; +import static org.assertj.core.api.Assertions.assertThat; + import com.somemore.domains.center.domain.Center; import com.somemore.domains.center.repository.center.CenterRepository; import com.somemore.domains.location.domain.Location; import com.somemore.domains.location.repository.LocationRepository; import com.somemore.domains.recruitboard.domain.RecruitBoard; import com.somemore.domains.recruitboard.domain.RecruitStatus; +import com.somemore.domains.recruitboard.domain.RecruitmentInfo; import com.somemore.domains.recruitboard.domain.VolunteerCategory; import com.somemore.domains.recruitboard.dto.condition.RecruitBoardNearByCondition; import com.somemore.domains.recruitboard.dto.condition.RecruitBoardSearchCondition; @@ -13,6 +23,11 @@ import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithCenter; import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithLocation; import com.somemore.support.IntegrationTestSupport; +import jakarta.persistence.EntityManager; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -23,21 +38,6 @@ import org.springframework.data.domain.Sort; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED; -import static com.somemore.domains.recruitboard.domain.VolunteerCategory.ADMINISTRATIVE_SUPPORT; -import static com.somemore.support.fixture.CenterFixture.createCenter; -import static com.somemore.support.fixture.LocalDateTimeFixture.createCurrentDateTime; -import static com.somemore.support.fixture.LocationFixture.createLocation; -import static com.somemore.support.fixture.RecruitBoardFixture.createCompletedRecruitBoard; -import static com.somemore.support.fixture.RecruitBoardFixture.createRecruitBoard; -import static org.assertj.core.api.Assertions.assertThat; - @Transactional class RecruitBoardRepositoryImplTest extends IntegrationTestSupport { @@ -50,66 +50,61 @@ class RecruitBoardRepositoryImplTest extends IntegrationTestSupport { @Autowired private LocationRepository locationRepository; - private final List boards = new ArrayList<>(); + @Autowired + private EntityManager em; - private UUID centerId; + private Location location; + private Center center; + private RecruitBoard board; @BeforeEach void setUp() { - Location location = createLocation(); + location = createLocation(); locationRepository.save(location); - Center center = createCenter(); + center = createCenter(); centerRepository.save(center); - centerId = center.getId(); - - for (int i = 1; i <= 100; i++) { - String title = "제목" + i; - RecruitBoard board = createRecruitBoard(title, center.getId(), location.getId()); - boards.add(board); - } - recruitBoardRepository.saveAll(boards); + + board = createRecruitBoard(center.getId(), location.getId(), RECRUITING); + recruitBoardRepository.save(board); } - @DisplayName("논리 삭제된 데이터를 id로 조회시 빈 Optional 반환된다") + @DisplayName("아이디로 모집글을 조회할 수 있다.") @Test void findById() { // given - RecruitBoard deletedBoard = createRecruitBoard(); - deletedBoard.markAsDeleted(); - recruitBoardRepository.save(deletedBoard); - - Long deletedId = deletedBoard.getId(); + Long id = board.getId(); // when - Optional findBoard = recruitBoardRepository.findById(deletedId); + Optional findBoard = recruitBoardRepository.findById(id); // then - assertThat(findBoard).isEmpty(); + assertThat(findBoard).isNotEmpty(); + assertThat(findBoard.get().getId()).isEqualTo(id); } - @DisplayName("존재하지 않는 아이디로 봉사 모집글과 작성기관을 조회하면 Optional.empty()가 반환된다.") + @DisplayName("아이디로 봉사 모집글과 작성기관을 조회할 수 있다.") @Test - void findWithCenterByIdWithNotExistId() { + void findWithLocationById() { // given - Location location = createLocation("특별한주소"); - locationRepository.save(location); - - RecruitBoard deletedRecruitBoard = createRecruitBoard(location.getId()); - deletedRecruitBoard.markAsDeleted(); - recruitBoardRepository.save(deletedRecruitBoard); + Long id = board.getId(); // when Optional findOne = recruitBoardRepository.findWithLocationById( - deletedRecruitBoard.getId()); + id); // then - assertThat(findOne).isEmpty(); + assertThat(findOne).isNotEmpty(); + RecruitBoardWithLocation boardWithLocation = findOne.get(); + assertThat(boardWithLocation.recruitBoard().getId()).isEqualTo(id); + assertThat(boardWithLocation.address()).isEqualTo(location.getAddress()); + assertThat(boardWithLocation.latitude()).isEqualByComparingTo(location.getLatitude()); + assertThat(boardWithLocation.longitude()).isEqualByComparingTo(location.getLongitude()); } @DisplayName("조건 없이 모집 게시글을 조회한다. (정렬 포함)") @Test - void findAllWithCenterWithoutCriteria() { + void findAllWithCenterWithoutCondition() { // given Pageable pageable = getPageable(); @@ -122,23 +117,17 @@ void findAllWithCenterWithoutCriteria() { // then assertThat(result).isNotNull(); - assertThat(result.getTotalElements()).isEqualTo(boards.size()); - assertThat(result.getSize()).isEqualTo(5); - assertThat(result.getNumber()).isZero(); - assertThat(result.getContent()).hasSize(5); - - assertThat(result.getContent().get(0).recruitBoard().getCreatedAt()) - .isAfterOrEqualTo(result.getContent().get(1).recruitBoard().getCreatedAt()); + assertThat(result.getTotalElements()).isEqualTo(1); } @DisplayName("키워드로 조회할 수 있다") @Test void findAllWithCenterByKeyword() { // given - Center center = createCenter(); - centerRepository.save(center); String keyword = "키워드"; - RecruitBoard recruitBoard = createRecruitBoard("키워드 조회 제목", center.getId()); + String title = keyword + " 제목"; + RecruitBoard recruitBoard = createRecruitBoard(center.getId(), title, OTHER, "지역", false, + RECRUITING); recruitBoardRepository.save(recruitBoard); Pageable pageable = getPageable(); @@ -153,26 +142,22 @@ void findAllWithCenterByKeyword() { // then assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(1); - assertThat(result.getSize()).isEqualTo(5); - assertThat(result.getNumber()).isZero(); assertThat(result.getContent()).hasSize(1); assertThat(result.getContent().getFirst().recruitBoard().getTitle()) - .isEqualTo("키워드 조회 제목"); + .isEqualTo(title); } @DisplayName("봉사활동 유형으로 조회할 수 있다") @Test void findAllWithCenterByCategory() { // given - Center center = createCenter(); - centerRepository.save(center); - - RecruitBoard recruitBoard = createRecruitBoard(ADMINISTRATIVE_SUPPORT, center.getId()); + VolunteerCategory category = ADMINISTRATIVE_SUPPORT; + RecruitBoard recruitBoard = createRecruitBoard(center.getId(), "제목", category, "지역", false, + RECRUITING); recruitBoardRepository.save(recruitBoard); Pageable pageable = getPageable(); - VolunteerCategory category = ADMINISTRATIVE_SUPPORT; RecruitBoardSearchCondition condition = RecruitBoardSearchCondition.builder() .category(category) .pageable(pageable) @@ -185,8 +170,6 @@ void findAllWithCenterByCategory() { assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(1); assertThat(result.getSize()).isEqualTo(5); - assertThat(result.getNumber()).isZero(); - assertThat(result.getContent()).hasSize(1); assertThat(result.getContent().getFirst().recruitBoard().getRecruitmentInfo() .getVolunteerCategory()).isEqualTo(category); @@ -196,12 +179,9 @@ void findAllWithCenterByCategory() { @Test void findAllWithCenterByRegion() { // given - Center center = createCenter(); - centerRepository.save(center); - String region = "특수지역"; - RecruitBoard recruitBoard = createRecruitBoard(center.getId()); - recruitBoard.updateWith(region); + RecruitBoard recruitBoard = createRecruitBoard(center.getId(), "제목", OTHER, region, false, + RECRUITING); recruitBoardRepository.save(recruitBoard); Pageable pageable = getPageable(); @@ -214,48 +194,10 @@ void findAllWithCenterByRegion() { // when Page result = recruitBoardRepository.findAllWithCenter(condition); - } - - @DisplayName("센터 ID로 완료되지 않은 모집 게시글들의 ID를 조회할 수 있다") - @Test - void findNotCompletedIdsByCenterIds() { - // given - UUID centerId = UUID.randomUUID(); - - RecruitBoard deletedRecruitingBoard = createRecruitBoard(centerId); - deletedRecruitingBoard.markAsDeleted(); - recruitBoardRepository.save(deletedRecruitingBoard); - - RecruitBoard recruitingBoard = createRecruitBoard(centerId); - recruitBoardRepository.save(recruitingBoard); - - RecruitBoard deletedClosedBoard = createRecruitBoard(centerId); - deletedClosedBoard.updateRecruitStatus(RecruitStatus.CLOSED); - deletedClosedBoard.markAsDeleted(); - recruitBoardRepository.save(deletedClosedBoard); - - RecruitBoard closedBoard = createRecruitBoard(centerId); - closedBoard.updateRecruitStatus(RecruitStatus.CLOSED); - recruitBoardRepository.save(closedBoard); - - RecruitBoard deletedCompletedRecruitBoard = createCompletedRecruitBoard(); - deletedCompletedRecruitBoard.markAsDeleted(); - recruitBoardRepository.save(deletedCompletedRecruitBoard); - - RecruitBoard completedRecruitBoard = createCompletedRecruitBoard(); - recruitBoardRepository.save(completedRecruitBoard); - - // when - List notCompletedBoardIds = recruitBoardRepository.findNotCompletedIdsByCenterId( - centerId); - // then - assertThat(notCompletedBoardIds) - .hasSize(2) - .doesNotContain(deletedRecruitingBoard.getId()) - .doesNotContain(deletedClosedBoard.getId()) - .doesNotContain(deletedCompletedRecruitBoard.getId()) - .doesNotContain(completedRecruitBoard.getId()); + assertThat(result).hasSize(1); + assertThat(result.getContent().getFirst().recruitBoard().getId()).isEqualTo( + recruitBoard.getId()); } @DisplayName("시간 인증 여부로 조회할 수 있다") @@ -265,14 +207,15 @@ void findAllWithCenterByAdmitted() { Center center = createCenter(); centerRepository.save(center); - Boolean admitted = false; - RecruitBoard recruitBoard = createRecruitBoard(admitted, center.getId()); + boolean admitted = false; + RecruitBoard recruitBoard = createRecruitBoard(center.getId(), "제목", OTHER, "지역", admitted, + RECRUITING); recruitBoardRepository.save(recruitBoard); Pageable pageable = getPageable(); RecruitBoardSearchCondition condition = RecruitBoardSearchCondition.builder() - .admitted(false) + .admitted(admitted) .pageable(pageable) .build(); @@ -298,9 +241,8 @@ void findAllWithCenterByStatus() { centerRepository.save(center); RecruitStatus status = CLOSED; - RecruitBoard recruitBoard = createRecruitBoard(center.getId()); - LocalDateTime currentDateTime = createCurrentDateTime(); - recruitBoard.updateRecruitStatus(status); + RecruitBoard recruitBoard = createRecruitBoard(center.getId(), "제목", OTHER, "지역", true, + status); recruitBoardRepository.save(recruitBoard); Pageable pageable = getPageable(); @@ -342,7 +284,7 @@ void findAllNearByLocation() { // then assertThat(result).isNotNull(); - assertThat(result.getTotalElements()).isEqualTo(boards.size()); + assertThat(result.getTotalElements()).isEqualTo(1); assertThat(result.getContent()).isNotEmpty(); } @@ -373,10 +315,8 @@ void findAllNearByLocation_noResult() { @Test void findAllByCenterId() { // given - Center center = createCenter(); - centerRepository.save(center); - - RecruitBoard recruitBoard = createRecruitBoard(center.getId()); + UUID centerId = UUID.randomUUID(); + RecruitBoard recruitBoard = createRecruitBoard(centerId, 1L, RECRUITING); recruitBoardRepository.save(recruitBoard); Pageable pageable = getPageable(); @@ -398,27 +338,51 @@ void findAllByCenterId() { @Test void findAllByCenterIdWhenWrongCenterId() { // given - UUID centerId = UUID.randomUUID(); + UUID wrongId = UUID.randomUUID(); Pageable pageable = getPageable(); RecruitBoardSearchCondition condition = RecruitBoardSearchCondition.builder() .pageable(pageable) .build(); // when - Page results = recruitBoardRepository.findAllByCenterId(centerId, - condition); + Page results = recruitBoardRepository.findAllByCenterId(wrongId, condition); // then assertThat(results).isEmpty(); } + @DisplayName("센터 ID로 완료되지 않은 모집 게시글들의 ID를 조회할 수 있다") + @Test + void findNotCompletedIdsByCenterIds() { + // given + UUID centerId = UUID.randomUUID(); + + RecruitBoard recruitingBoard = createRecruitBoard(centerId, 1L, RECRUITING); + recruitBoardRepository.save(recruitingBoard); + + RecruitBoard closedBoard = createRecruitBoard(centerId, 1L, CLOSED); + recruitBoardRepository.save(closedBoard); + + RecruitBoard completedRecruitBoard = createRecruitBoard(centerId, 1L, COMPLETED); + recruitBoardRepository.save(completedRecruitBoard); + + // when + List notCompletedBoardIds = recruitBoardRepository.findNotCompletedIdsByCenterId( + centerId); + + // then + assertThat(notCompletedBoardIds) + .hasSize(2) + .doesNotContain(completedRecruitBoard.getId()); + } + @DisplayName("아이디 리스트로 모집글을 조회할 수 있다.") @Test void findAllByIds() { // given - RecruitBoard board1 = createRecruitBoard(); - RecruitBoard board2 = createRecruitBoard(); - RecruitBoard board3 = createRecruitBoard(); + RecruitBoard board1 = createRecruitBoard(UUID.randomUUID(), 1L, RECRUITING); + RecruitBoard board2 = createRecruitBoard(UUID.randomUUID(), 1L, RECRUITING); + RecruitBoard board3 = createRecruitBoard(UUID.randomUUID(), 1L, RECRUITING); recruitBoardRepository.saveAll(List.of(board1, board2, board3)); List ids = List.of(board1.getId(), board2.getId(), board3.getId(), 100000L); @@ -430,6 +394,57 @@ void findAllByIds() { assertThat(all).hasSize(3); } + @DisplayName("봉사 시작일 기준으로 모집중 상태 게시글을 모집 완료로 변경한다") + @Test + void updateRecruitingToClosedByStartDate() { + // given + LocalDateTime now = LocalDateTime.of(2024, 1, 1, 0, 0); // 2024-01-01 00:00:00 + LocalDateTime nextDay = now.plusDays(1); // 2024-01-02 00:00:00 + LocalDateTime startDateTime = now.plusHours(12); // 2024-01-01 12:00:00 + LocalDateTime endDateTime = startDateTime.plusHours(2); // 2024-01-01 14:00:00 + + RecruitBoard boardOne = createRecruitBoard(startDateTime, endDateTime, RECRUITING); + RecruitBoard boardTwo = createRecruitBoard(startDateTime, endDateTime, RECRUITING); + recruitBoardRepository.saveAll(List.of(boardOne, boardTwo)); + + // when + long updateCnt = recruitBoardRepository.updateRecruitingToClosedByStartDate(now, nextDay); + em.clear(); + + // then + assertThat(updateCnt).isEqualTo(2); + RecruitBoard one = recruitBoardRepository.findById(boardOne.getId()).orElseThrow(); + RecruitBoard two = recruitBoardRepository.findById(boardTwo.getId()).orElseThrow(); + assertThat(one.getRecruitStatus()).isEqualTo(CLOSED); + assertThat(two.getRecruitStatus()).isEqualTo(CLOSED); + } + + @DisplayName("봉사 종료일 기준으로 모집완료 상태 게시글을 종료로 변경한다") + @Test + void updateClosedToCompletedByEndDate() { + // given + LocalDateTime now = LocalDateTime.of(2024, 1, 2, 0, 0); // 2024-01-02 00:00:00 + LocalDateTime yesterday = now.minusDays(1); // 2024-01-01 00:00:00 + LocalDateTime startDateTime = yesterday.plusHours(12); // 2024-01-01 12:00:00 + LocalDateTime endDateTime = startDateTime.plusHours(2); // 2024-01-01 14:00:00 + + RecruitBoard boardOne = createRecruitBoard(startDateTime, endDateTime, CLOSED); + RecruitBoard boardTwo = createRecruitBoard(startDateTime, endDateTime, CLOSED); + recruitBoardRepository.saveAll(List.of(boardOne, boardTwo)); + + // when + long updateCnt = recruitBoardRepository.updateClosedToCompletedByEndDate(now); + em.clear(); + + // then + assertThat(updateCnt).isEqualTo(2); + + RecruitBoard one = recruitBoardRepository.findById(boardOne.getId()).orElseThrow(); + RecruitBoard two = recruitBoardRepository.findById(boardTwo.getId()).orElseThrow(); + assertThat(one.getRecruitStatus()).isEqualTo(COMPLETED); + assertThat(two.getRecruitStatus()).isEqualTo(COMPLETED); + } + // @DisplayName("모집글을 elastic search index에 저장할 수 있다. (repository)") // @Test // void saveDocuments() { @@ -462,6 +477,79 @@ void findAllByIds() { // recruitBoardRepository.deleteDocument(savedBoard2.getId()); // } + public static RecruitBoard createRecruitBoard(UUID centerId, Long locationId, + RecruitStatus status) { + + RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() + .region("지역") + .recruitmentCount(1) + .volunteerStartDateTime(LocalDateTime.now()) + .volunteerEndDateTime(LocalDateTime.now()) + .volunteerHours(10) + .volunteerCategory(OTHER) + .admitted(true) + .build(); + + return RecruitBoard.builder() + .centerId(centerId) + .locationId(locationId) + .title("모집글 제목") + .content("모집글 내용") + .imgUrl("이미지 링크") + .recruitmentInfo(recruitmentInfo) + .status(status) + .build(); + } + + public static RecruitBoard createRecruitBoard(UUID centerId, String title, + VolunteerCategory category, + String region, boolean admitted, RecruitStatus status) { + + RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() + .region(region) + .recruitmentCount(1) + .volunteerStartDateTime(LocalDateTime.now()) + .volunteerEndDateTime(LocalDateTime.now()) + .volunteerHours(10) + .volunteerCategory(category) + .admitted(admitted) + .build(); + + return RecruitBoard.builder() + .centerId(centerId) + .locationId(1L) + .title(title) + .content("모집글 내용") + .imgUrl("이미지 링크") + .recruitmentInfo(recruitmentInfo) + .status(status) + .build(); + } + + public static RecruitBoard createRecruitBoard(LocalDateTime startTime, LocalDateTime endTime, + RecruitStatus status) { + + RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() + .region("지역") + .recruitmentCount(1) + .volunteerStartDateTime(startTime) + .volunteerEndDateTime(endTime) + .volunteerHours(10) + .volunteerCategory(OTHER) + .admitted(true) + .build(); + + return RecruitBoard.builder() + .centerId(UUID.randomUUID()) + .locationId(1L) + .title("모집글 제목") + .content("모집글 내용") + .imgUrl("이미지 링크") + .recruitmentInfo(recruitmentInfo) + .status(status) + .build(); + } + private Pageable getPageable() { Sort sort = Sort.by(Sort.Order.desc("created_at")); return PageRequest.of(0, 5, sort); diff --git a/src/test/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateSchedulerTest.java b/src/test/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateSchedulerTest.java new file mode 100644 index 000000000..ad2bfbd1e --- /dev/null +++ b/src/test/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateSchedulerTest.java @@ -0,0 +1,106 @@ +package com.somemore.domains.recruitboard.scheduler; + +import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.COMPLETED; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; +import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; +import static org.assertj.core.api.Assertions.assertThat; + +import com.somemore.domains.recruitboard.domain.RecruitBoard; +import com.somemore.domains.recruitboard.domain.RecruitStatus; +import com.somemore.domains.recruitboard.domain.RecruitmentInfo; +import com.somemore.domains.recruitboard.repository.RecruitBoardRepository; +import com.somemore.support.IntegrationTestSupport; +import jakarta.persistence.EntityManager; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +class RecruitBoardStatusUpdateSchedulerTest extends IntegrationTestSupport { + + @Autowired + private RecruitBoardRepository recruitBoardRepository; + + @Autowired + private RecruitBoardStatusUpdateScheduler scheduler; + + @Autowired + private EntityManager em; + + @DisplayName("봉사 시작일에 해당하는 모집글 상태를 CLOSED 변경") + @Test + void updateRecruitBoardStatusToClosed() { + // given + LocalDateTime today = LocalDate.now().atStartOfDay(); // today 00:00:00 + LocalDateTime startDateTime = today.plusHours(12); // today 12:00:00 + LocalDateTime endDateTime = startDateTime.plusHours(2); // today 14:00:00 + + RecruitBoard boardOne = createRecruitBoard(startDateTime, endDateTime, RECRUITING); + RecruitBoard boardTwo = createRecruitBoard(startDateTime, endDateTime, RECRUITING); + recruitBoardRepository.saveAll(List.of(boardOne, boardTwo)); + + // when + scheduler.updateRecruitBoardStatusToClosed(); + em.clear(); + + // then + RecruitBoard one = recruitBoardRepository.findById(boardOne.getId()).orElseThrow(); + RecruitBoard two = recruitBoardRepository.findById(boardTwo.getId()).orElseThrow(); + assertThat(one.getRecruitStatus()).isEqualTo(CLOSED); + assertThat(two.getRecruitStatus()).isEqualTo(CLOSED); + } + + @DisplayName("봉사 종료일 기준으로 모집완료 상태 게시글을 종료로 변경한다") + @Test + void updateRecruitBoardStatusToCompleted() { + // given + LocalDateTime now = LocalDate.now().atStartOfDay(); // today 00:00:00 + LocalDateTime yesterday = now.minusDays(1); // yesterday 00:00:00 + LocalDateTime startDateTime = yesterday.plusHours(12); // yesterday 12:00:00 + LocalDateTime endDateTime = startDateTime.plusHours(2); // yesterday 14:00:00 + + RecruitBoard boardOne = createRecruitBoard(startDateTime, endDateTime, CLOSED); + RecruitBoard boardTwo = createRecruitBoard(startDateTime, endDateTime, CLOSED); + recruitBoardRepository.saveAll(List.of(boardOne, boardTwo)); + + // when + scheduler.updateRecruitBoardStatusToCompleted(); + em.clear(); + + // then + RecruitBoard one = recruitBoardRepository.findById(boardOne.getId()).orElseThrow(); + RecruitBoard two = recruitBoardRepository.findById(boardTwo.getId()).orElseThrow(); + assertThat(one.getRecruitStatus()).isEqualTo(COMPLETED); + assertThat(two.getRecruitStatus()).isEqualTo(COMPLETED); + } + + public static RecruitBoard createRecruitBoard(LocalDateTime startTime, LocalDateTime endTime, + RecruitStatus status) { + + RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() + .region("지역") + .recruitmentCount(1) + .volunteerStartDateTime(startTime) + .volunteerEndDateTime(endTime) + .volunteerHours(10) + .volunteerCategory(OTHER) + .admitted(true) + .build(); + + return RecruitBoard.builder() + .centerId(UUID.randomUUID()) + .locationId(1L) + .title("모집글 제목") + .content("모집글 내용") + .imgUrl("이미지 링크") + .recruitmentInfo(recruitmentInfo) + .status(status) + .build(); + } +} From 0a1766f063062a0d930ccb3ae4f19b0e8f0b4644 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 14:35:56 +0900 Subject: [PATCH 12/25] =?UTF-8?q?refactor(recruit-board):=20sonarqube=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecruitBoardRepositoryImpl.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java index 83de26e6e..a6caf0927 100644 --- a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java +++ b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java @@ -210,24 +210,27 @@ public List findAll() { } @Override - public long updateRecruitingToClosedByStartDate(LocalDateTime startTime, LocalDateTime endTime) { - BooleanExpression exp = statusEq(RECRUITING) - .and(volunteerStartDateTimeBetween(startTime, endTime)); - + public long updateRecruitingToClosedByStartDate(LocalDateTime startTime, + LocalDateTime endTime) { return queryFactory.update(recruitBoard) .set(recruitBoard.recruitStatus, CLOSED) - .where(exp) + .where( + statusEq(RECRUITING), + volunteerStartDateTimeBetween(startTime, endTime), + isNotDeleted() + ) .execute(); } @Override public long updateClosedToCompletedByEndDate(LocalDateTime now) { - BooleanExpression exp = statusEq(CLOSED) - .and(volunteerEndDateTimeBefore(now)); - return queryFactory.update(recruitBoard) .set(recruitBoard.recruitStatus, COMPLETED) - .where(exp) + .where( + statusEq(CLOSED), + volunteerEndDateTimeBefore(now), + isNotDeleted() + ) .execute(); } From a6f10dea2b7801c7cf92ef5d60492f277e4ada89 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 14:49:02 +0900 Subject: [PATCH 13/25] =?UTF-8?q?fix(recruit-board):=20sonarqube=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/RecruitBoardRepositoryImplTest.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java index 2a4f64a86..fca60d3a4 100644 --- a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java @@ -102,7 +102,7 @@ void findWithLocationById() { assertThat(boardWithLocation.longitude()).isEqualByComparingTo(location.getLongitude()); } - @DisplayName("조건 없이 모집 게시글을 조회한다. (정렬 포함)") + @DisplayName("조건 없이 모집 게시글을 조회한다.") @Test void findAllWithCenterWithoutCondition() { // given @@ -204,9 +204,6 @@ void findAllWithCenterByRegion() { @Test void findAllWithCenterByAdmitted() { // given - Center center = createCenter(); - centerRepository.save(center); - boolean admitted = false; RecruitBoard recruitBoard = createRecruitBoard(center.getId(), "제목", OTHER, "지역", admitted, RECRUITING); @@ -237,9 +234,6 @@ void findAllWithCenterByAdmitted() { @Test void findAllWithCenterByStatus() { // given - Center center = createCenter(); - centerRepository.save(center); - RecruitStatus status = CLOSED; RecruitBoard recruitBoard = createRecruitBoard(center.getId(), "제목", OTHER, "지역", true, status); From 38f4f04c0b340951346c4f68a9f347230db78948 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 14:49:53 +0900 Subject: [PATCH 14/25] =?UTF-8?q?fix(recruit-board):=20sonarqube=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/validator/RecruitBoardValidatorTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java b/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java index 1eb830958..f8307ab2b 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java @@ -10,7 +10,6 @@ import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD; import static com.somemore.support.fixture.LocalDateTimeFixture.createCurrentDateTime; import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; -import static com.somemore.support.fixture.RecruitBoardFixture.createRecruitBoard; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -32,6 +31,7 @@ class RecruitBoardValidatorTest { private final RecruitBoardValidator validator = new RecruitBoardValidator(); private RecruitBoard board; + @BeforeEach void setUp() { board = createRecruitBoard(); @@ -89,7 +89,6 @@ void validateUpdateRecruitBoardTimeWhenNotValid() { void validateWriter() { // given UUID wrongCenterId = UUID.randomUUID(); - RecruitBoard board = createRecruitBoard(); // when // then From 7cc768b9056cc4408569cd7e72a914b80d60faaa Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 15:33:03 +0900 Subject: [PATCH 15/25] =?UTF-8?q?refactor(recruit-board):=20=EC=8A=A4?= =?UTF-8?q?=EC=BC=80=EC=A5=B4=EB=9F=AC=20retryable=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20=20-=20=EC=B5=9C=EB=8C=80=20=EC=8B=9C?= =?UTF-8?q?=EB=8F=84,=20=EC=A3=BC=EA=B8=B0=20=EB=AA=85=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheduler/RecruitBoardStatusUpdateScheduler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java index 41a9cd324..bf9d23138 100644 --- a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java +++ b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java @@ -21,7 +21,8 @@ public class RecruitBoardStatusUpdateScheduler { @Retryable( retryFor = Exception.class, - backoff = @Backoff(delay = 100000) + maxAttempts = 3, + backoff = @Backoff(delay = 2000) ) @Scheduled(cron = "0 0 0 * * ?") public void updateRecruitBoardStatusToClosed() { @@ -37,7 +38,8 @@ public void updateRecruitBoardStatusToClosed() { @Retryable( retryFor = Exception.class, - backoff = @Backoff(delay = 100000) + maxAttempts = 3, + backoff = @Backoff(delay = 2000) ) @Scheduled(cron = "0 0 0 * * ?") public void updateRecruitBoardStatusToCompleted() { From 6a346e04c925c63fffdd2f87bd17865a5f51e3b6 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 17:20:38 +0900 Subject: [PATCH 16/25] =?UTF-8?q?fix(recruit-board):=20=EC=98=A4=ED=83=80?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/validator/RecruitBoardValidatorTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java b/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java index f8307ab2b..212abcf83 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/validator/RecruitBoardValidatorTest.java @@ -55,7 +55,7 @@ void validateRecruitBoardTimeWhenNotValid(long minutesOffset) { } @Test - @DisplayName("StartDateTime이 createdAt + 1일 이후일 경우 예외 없이 통과") + @DisplayName("StartDateTime createdAt + 1일 이후일 경우 예외 없이 통과") void validateUpdateRecruitBoardTime() { // given LocalDateTime createdAt = LocalDateTime.of(2025, 1, 1, 10, 0); // 2025-01-01 10:00:00 @@ -68,12 +68,12 @@ void validateUpdateRecruitBoardTime() { } @Test - @DisplayName("StartDateTime이 createdAt + 1일 이전일 경우 예외 발생") + @DisplayName("StartDateTime createdAt + 1일 이전일 경우 예외 발생") void validateUpdateRecruitBoardTimeWhenNotValid() { // given - LocalDateTime createdAt = LocalDateTime.of(2025, 1, 2, 10, 0); // 2025-1-1 10:00:00 - LocalDateTime start = LocalDateTime.of(2025, 1, 2, 12, 0); // 2025-1-2 12:00:00 - LocalDateTime end = LocalDateTime.of(2025, 1, 2, 14, 0); // 2025-1-2 14:00:00 + LocalDateTime createdAt = LocalDateTime.of(2025, 1, 2, 10, 0); // 2025-01-02 10:00:00 + LocalDateTime start = LocalDateTime.of(2025, 1, 2, 12, 0); // 2025-01-02 12:00:00 + LocalDateTime end = LocalDateTime.of(2025, 1, 2, 14, 0); // 2025-01-02 14:00:00 // when // then From 97c9e20a394b7dda3bd88c427445f45a8ef623b5 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Tue, 14 Jan 2025 17:27:43 +0900 Subject: [PATCH 17/25] =?UTF-8?q?refactor(recruit-board):=20=EC=8A=A4?= =?UTF-8?q?=EC=BC=80=EC=A5=B4=EB=A7=81=20=EC=9E=91=EC=97=85=20=EC=A4=91=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EB=B0=9C=EC=83=9D=EC=8B=9C=20=EB=A1=9C?= =?UTF-8?q?=EA=B9=85=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecruitBoardStatusUpdateScheduler.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java index bf9d23138..354682c7d 100644 --- a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java +++ b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java @@ -30,10 +30,14 @@ public void updateRecruitBoardStatusToClosed() { LocalDateTime startOfDay = LocalDate.now().atStartOfDay(); LocalDateTime startOfNextDay = LocalDate.now().plusDays(1).atStartOfDay(); - long updatedCount = recruitBoardRepository.updateRecruitingToClosedByStartDate(startOfDay, - startOfNextDay); - log.info("총 {}개의 모집글 상태를 CLOSED로 변경 완료", updatedCount); - + try { + long updatedCount = recruitBoardRepository.updateRecruitingToClosedByStartDate( + startOfDay, startOfNextDay); + log.info("총 {}개의 모집글 상태를 CLOSED로 변경 완료", updatedCount); + } catch (Exception e) { + log.error("봉사 시작일에 해당하는 모집글 상태를 CLOSED로 변경하는 중 오류 발생", e); + throw e; + } } @Retryable( @@ -46,9 +50,13 @@ public void updateRecruitBoardStatusToCompleted() { log.info("봉사 종료일이 지난 모집글 상태를 COMPLETED로 변경하는 작업 시작"); LocalDateTime now = LocalDateTime.now(); - long updatedCount = recruitBoardRepository.updateClosedToCompletedByEndDate(now); - log.info("총 {}개의 모집글 상태를 COMPLETED로 변경 완료", updatedCount); - + try { + long updatedCount = recruitBoardRepository.updateClosedToCompletedByEndDate(now); + log.info("총 {}개의 모집글 상태를 COMPLETED로 변경 완료", updatedCount); + } catch (Exception e) { + log.error("봉사 종료일이 지난 모집글 상태를 COMPLETED로 변경하는 중 오류 발생", e); + throw e; + } } } From b8b4c81ba0ad05a96d127fb46386a0324780e225 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Wed, 15 Jan 2025 15:13:50 +0900 Subject: [PATCH 18/25] =?UTF-8?q?feat:=20TimeConfig=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/global/config/TimeConfig.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/com/somemore/global/config/TimeConfig.java diff --git a/src/main/java/com/somemore/global/config/TimeConfig.java b/src/main/java/com/somemore/global/config/TimeConfig.java new file mode 100644 index 000000000..6d77ddf42 --- /dev/null +++ b/src/main/java/com/somemore/global/config/TimeConfig.java @@ -0,0 +1,14 @@ +package com.somemore.global.config; + +import java.time.Clock; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TimeConfig { + + @Bean + public Clock clock() { + return Clock.systemDefaultZone(); + } +} From e085aed1847e7bcc8000b20ba8938d423af344c1 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Wed, 15 Jan 2025 15:15:11 +0900 Subject: [PATCH 19/25] =?UTF-8?q?refactor(recruit-board):=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=20-=20Service=20=EA=B3=84=EC=B8=B5=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Clock=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecruitBoardCommandApiController.java | 31 +++++++++---------- .../service/UpdateRecruitBoardService.java | 19 ++++++------ .../usecase/UpdateRecruitBoardUseCase.java | 11 +++---- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiController.java b/src/main/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiController.java index 4f236d8c0..9bc2fdfed 100644 --- a/src/main/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiController.java +++ b/src/main/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiController.java @@ -1,6 +1,8 @@ package com.somemore.domains.recruitboard.controller; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; + import com.somemore.domains.recruitboard.dto.request.RecruitBoardCreateRequestDto; import com.somemore.domains.recruitboard.dto.request.RecruitBoardLocationUpdateRequestDto; import com.somemore.domains.recruitboard.dto.request.RecruitBoardStatusUpdateRequestDto; @@ -15,16 +17,20 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.security.access.annotation.Secured; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import java.time.LocalDateTime; -import java.util.UUID; - -import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; - @Tag(name = "Recruit Board Command API", description = "봉사 활동 모집글 생성 수정 삭제 API") @RequiredArgsConstructor @RequestMapping("/api") @@ -44,7 +50,6 @@ public ApiResponse createRecruitBoard( @Valid @RequestPart("data") RecruitBoardCreateRequestDto requestDto, @RequestPart(value = "img_file", required = false) MultipartFile image ) { - String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image)); return ApiResponse.ok( 201, @@ -63,9 +68,7 @@ public ApiResponse updateRecruitBoard( @RequestPart(value = "img_file", required = false) MultipartFile image ) { String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image)); - LocalDateTime now = LocalDateTime.now(); - updateRecruitBoardUseCase.updateRecruitBoard(requestDto, id, userId, imgUrl, now); - + updateRecruitBoardUseCase.updateRecruitBoard(requestDto, id, userId, imgUrl); return ApiResponse.ok("봉사 활동 모집글 수정 성공"); } @@ -77,8 +80,7 @@ public ApiResponse updateRecruitBoardLocation( @PathVariable Long id, @Valid @RequestBody RecruitBoardLocationUpdateRequestDto requestDto ) { - LocalDateTime now = LocalDateTime.now(); - updateRecruitBoardUseCase.updateRecruitBoardLocation(requestDto, id, userId, now); + updateRecruitBoardUseCase.updateRecruitBoardLocation(requestDto, id, userId); return ApiResponse.ok("봉사 활동 모집글 위치 수정 성공"); } @@ -90,9 +92,7 @@ public ApiResponse updateRecruitBoardStatus( @PathVariable Long id, @RequestBody RecruitBoardStatusUpdateRequestDto requestDto ) { - LocalDateTime now = LocalDateTime.now(); - updateRecruitBoardUseCase.updateRecruitBoardStatus(requestDto.status(), id, userId, now); - + updateRecruitBoardUseCase.updateRecruitBoardStatus(requestDto.status(), id, userId); return ApiResponse.ok("봉사 활동 모집글 상태 수정 성공"); } @@ -106,5 +106,4 @@ public ApiResponse deleteRecruitBoard( deleteRecruitBoardUseCase.deleteRecruitBoard(userId, id); return ApiResponse.ok("봉사 활동 모집글 삭제 성공"); } - } diff --git a/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java b/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java index d9215ebe1..c8d644864 100644 --- a/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java +++ b/src/main/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardService.java @@ -8,6 +8,7 @@ import com.somemore.domains.recruitboard.service.validator.RecruitBoardValidator; import com.somemore.domains.recruitboard.usecase.RecruitBoardQueryUseCase; import com.somemore.domains.recruitboard.usecase.UpdateRecruitBoardUseCase; +import java.time.Clock; import java.time.LocalDateTime; import java.util.UUID; import lombok.RequiredArgsConstructor; @@ -22,12 +23,13 @@ public class UpdateRecruitBoardService implements UpdateRecruitBoardUseCase { private final RecruitBoardQueryUseCase recruitBoardQueryUseCase; private final UpdateLocationUseCase updateLocationUseCase; private final RecruitBoardValidator recruitBoardValidator; + private final Clock clock; @Override public void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID centerId, - String imgUrl, LocalDateTime current) { + String imgUrl) { RecruitBoard recruitBoard = getRecruitBoard(id); - validateUpdatableAndWriter(recruitBoard, centerId, current); + validateUpdatableAndWriter(recruitBoard, centerId); recruitBoardValidator.validateUpdateRecruitBoardTime(recruitBoard.getCreatedAt(), dto.volunteerStartDateTime(), dto.volunteerEndDateTime()); @@ -37,9 +39,9 @@ public void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID c @Override public void updateRecruitBoardLocation(RecruitBoardLocationUpdateRequestDto requestDto, Long id, - UUID centerId, LocalDateTime current) { + UUID centerId) { RecruitBoard recruitBoard = getRecruitBoard(id); - validateUpdatableAndWriter(recruitBoard, centerId, current); + validateUpdatableAndWriter(recruitBoard, centerId); updateLocationUseCase.updateLocation(requestDto.toLocationUpdateRequestDto(), recruitBoard.getLocationId()); @@ -47,17 +49,16 @@ public void updateRecruitBoardLocation(RecruitBoardLocationUpdateRequestDto requ } @Override - public void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerId, - LocalDateTime current) { + public void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerId) { RecruitBoard recruitBoard = getRecruitBoard(id); - validateUpdatableAndWriter(recruitBoard, centerId, current); + validateUpdatableAndWriter(recruitBoard, centerId); recruitBoardValidator.validateRecruitStatus(status); recruitBoard.updateRecruitStatus(status); } - private void validateUpdatableAndWriter(RecruitBoard recruitBoard, UUID centerId, - LocalDateTime current) { + private void validateUpdatableAndWriter(RecruitBoard recruitBoard, UUID centerId) { + LocalDateTime current = LocalDateTime.now(clock); recruitBoardValidator.validateUpdatable(recruitBoard, current); recruitBoardValidator.validateWriter(recruitBoard, centerId); } diff --git a/src/main/java/com/somemore/domains/recruitboard/usecase/UpdateRecruitBoardUseCase.java b/src/main/java/com/somemore/domains/recruitboard/usecase/UpdateRecruitBoardUseCase.java index 4cb318fe6..968dd74cf 100644 --- a/src/main/java/com/somemore/domains/recruitboard/usecase/UpdateRecruitBoardUseCase.java +++ b/src/main/java/com/somemore/domains/recruitboard/usecase/UpdateRecruitBoardUseCase.java @@ -3,18 +3,15 @@ import com.somemore.domains.recruitboard.domain.RecruitStatus; import com.somemore.domains.recruitboard.dto.request.RecruitBoardLocationUpdateRequestDto; import com.somemore.domains.recruitboard.dto.request.RecruitBoardUpdateRequestDto; -import java.time.LocalDateTime; import java.util.UUID; public interface UpdateRecruitBoardUseCase { - void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID centerId, String imgUrl, - LocalDateTime current); - + void updateRecruitBoard(RecruitBoardUpdateRequestDto dto, Long id, UUID centerId, + String imgUrl); void updateRecruitBoardLocation(RecruitBoardLocationUpdateRequestDto requestDto, Long id, - UUID centerId, LocalDateTime current); + UUID centerId); - void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerId, - LocalDateTime currentDateTime); + void updateRecruitBoardStatus(RecruitStatus status, Long id, UUID centerId); } From 373638bdd93ad746d206e631a9338914c4e64ad8 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Wed, 15 Jan 2025 15:15:20 +0900 Subject: [PATCH 20/25] =?UTF-8?q?test(recruit-board):=20=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=20-=20Service=20=EA=B3=84?= =?UTF-8?q?=EC=B8=B5=EC=97=90=EC=84=9C=20Clock=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecruitBoardCommandApiControllerTest.java | 6 +-- .../UpdateRecruitBoardServiceTest.java | 53 +++++++++++++------ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiControllerTest.java b/src/test/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiControllerTest.java index 6c1e2e811..1081d5a8c 100644 --- a/src/test/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiControllerTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/controller/RecruitBoardCommandApiControllerTest.java @@ -150,7 +150,7 @@ void updateRecruitBoard() throws Exception { given(imageUploadUseCase.uploadImage(any())).willReturn(mockImageUrl); willDoNothing().given(updateRecruitBoardUseCase) - .updateRecruitBoard(any(), any(), any(UUID.class), anyString(), any()); + .updateRecruitBoard(any(), any(), any(UUID.class), anyString()); MockMultipartHttpServletRequestBuilder builder = multipart("/api/recruit-board/{id}", 1); builder.with(new RequestPostProcessor() { @@ -187,7 +187,7 @@ void updateRecruitBoardLocation() throws Exception { .build(); willDoNothing().given(updateRecruitBoardUseCase) - .updateRecruitBoardLocation(any(), any(), any(UUID.class), any()); + .updateRecruitBoardLocation(any(), any(), any(UUID.class)); String requestBody = objectMapper.writeValueAsString(requestDto); @@ -213,7 +213,7 @@ void updateRecruitBoardStatus() throws Exception { status); String requestBody = objectMapper.writeValueAsString(dto); willDoNothing().given(updateRecruitBoardUseCase) - .updateRecruitBoardStatus(any(), any(), any(UUID.class), any(LocalDateTime.class)); + .updateRecruitBoardStatus(any(), any(), any(UUID.class)); // when mockMvc.perform(patch("/api/recruit-board/{id}", 1L) diff --git a/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java b/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java index fbe50f4ab..52fd9285f 100644 --- a/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/service/UpdateRecruitBoardServiceTest.java @@ -1,5 +1,14 @@ package com.somemore.domains.recruitboard.service; +import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; +import static com.somemore.domains.recruitboard.domain.VolunteerCategory.ADMINISTRATIVE_SUPPORT; +import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; +import static com.somemore.support.fixture.LocalDateTimeFixture.createCurrentDateTime; +import static com.somemore.support.fixture.LocalDateTimeFixture.createStartDateTime; +import static com.somemore.support.fixture.LocalDateTimeFixture.createUpdateStartDateTime; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; + import com.somemore.domains.location.domain.Location; import com.somemore.domains.location.repository.LocationRepository; import com.somemore.domains.recruitboard.domain.RecruitBoard; @@ -10,22 +19,18 @@ import com.somemore.domains.recruitboard.repository.RecruitBoardJpaRepository; import com.somemore.domains.recruitboard.repository.RecruitBoardRepository; import com.somemore.support.IntegrationTestSupport; +import java.math.BigDecimal; +import java.time.Clock; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.transaction.annotation.Transactional; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.UUID; - -import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING; -import static com.somemore.domains.recruitboard.domain.VolunteerCategory.ADMINISTRATIVE_SUPPORT; -import static com.somemore.domains.recruitboard.domain.VolunteerCategory.OTHER; -import static com.somemore.support.fixture.LocalDateTimeFixture.*; -import static org.assertj.core.api.Assertions.assertThat; - @Transactional class UpdateRecruitBoardServiceTest extends IntegrationTestSupport { @@ -41,7 +46,11 @@ class UpdateRecruitBoardServiceTest extends IntegrationTestSupport { @Autowired private LocationRepository locationRepository; + @SpyBean + private Clock clock; + private RecruitBoard recruitBoard; + private UUID centerId; @BeforeEach @@ -73,8 +82,11 @@ void updateRecruitBoard() { .admitted(false) .build(); + setMockClock(current); + // when - updateRecruitBoardService.updateRecruitBoard(dto, recruitBoard.getId(), centerId, newImgUrl, current); + updateRecruitBoardService.updateRecruitBoard(dto, recruitBoard.getId(), centerId, + newImgUrl); // then RecruitBoard updatedRecruitBoard = recruitBoardRepository.findById(recruitBoard.getId()) @@ -107,8 +119,10 @@ void updateRecruitBoardLocation() { .longitude(BigDecimal.valueOf(127.2222222)) .build(); + setMockClock(current); + // when - updateRecruitBoardService.updateRecruitBoardLocation(dto, recruitBoard.getId(), centerId, current); + updateRecruitBoardService.updateRecruitBoardLocation(dto, recruitBoard.getId(), centerId); // then RecruitBoard updateRecruitBoard = recruitBoardRepository.findById(recruitBoard.getId()) @@ -130,10 +144,12 @@ void updateRecruitBoardStatus() { // given Long recruitBoardId = recruitBoard.getId(); RecruitStatus newStatus = RecruitStatus.CLOSED; - LocalDateTime currentDateTime = createCurrentDateTime(); + LocalDateTime current = createCurrentDateTime(); + + setMockClock(current); // when - updateRecruitBoardService.updateRecruitBoardStatus(newStatus, recruitBoardId, centerId, currentDateTime); + updateRecruitBoardService.updateRecruitBoardStatus(newStatus, recruitBoardId, centerId); // then RecruitBoard findBoard = recruitBoardRepository.findById(recruitBoardId).orElseThrow(); @@ -147,7 +163,8 @@ private static RecruitBoard createRecruitBoard(UUID centerId, Long locationId) { return createRecruitBoard(centerId, locationId, startDateTime, endDateTime); } - private static RecruitBoard createRecruitBoard(UUID centerId, Long locationId, LocalDateTime startDateTime, LocalDateTime endDateTime) { + private static RecruitBoard createRecruitBoard(UUID centerId, Long locationId, + LocalDateTime startDateTime, LocalDateTime endDateTime) { RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() .region("경기") @@ -177,4 +194,10 @@ private static Location createLocation() { .latitude(BigDecimal.valueOf(127.11111)) .build(); } + + private void setMockClock(LocalDateTime current) { + doReturn(current.atZone(ZoneId.systemDefault()).toInstant()) + .when(clock) + .instant(); + } } From 8793b85cb2f624420cf698939538e1a462ca9eae Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Wed, 15 Jan 2025 16:19:10 +0900 Subject: [PATCH 21/25] =?UTF-8?q?refactor(recruit-board):=20=EC=8A=A4?= =?UTF-8?q?=EC=BC=80=EC=A5=B4=EB=A7=81=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20=20-=20between()=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/RecruitBoardRepository.java | 2 +- .../repository/RecruitBoardRepositoryImpl.java | 9 +++++---- .../scheduler/RecruitBoardStatusUpdateScheduler.java | 11 ++++++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java index 68a5e3934..991a10b49 100644 --- a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java +++ b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java @@ -36,7 +36,7 @@ public interface RecruitBoardRepository { long updateRecruitingToClosedByStartDate(LocalDateTime startTime, LocalDateTime endTime); - long updateClosedToCompletedByEndDate(LocalDateTime now); + long updateClosedToCompletedByEndDate(LocalDateTime startTime, LocalDateTime endTime); // Page findAllNearbyWithKeyword(RecruitBoardNearByCondition condition); // Page findByRecruitBoardsContaining(RecruitBoardSearchCondition condition); diff --git a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java index a6caf0927..c56019155 100644 --- a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java +++ b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java @@ -223,12 +223,12 @@ public long updateRecruitingToClosedByStartDate(LocalDateTime startTime, } @Override - public long updateClosedToCompletedByEndDate(LocalDateTime now) { + public long updateClosedToCompletedByEndDate(LocalDateTime startTime, LocalDateTime endTime) { return queryFactory.update(recruitBoard) .set(recruitBoard.recruitStatus, COMPLETED) .where( statusEq(CLOSED), - volunteerEndDateTimeBefore(now), + volunteerEndDateTimeBetween(startTime, endTime), isNotDeleted() ) .execute(); @@ -389,8 +389,9 @@ private static BooleanExpression volunteerStartDateTimeBetween(LocalDateTime sta return recruitBoard.recruitmentInfo.volunteerStartDateTime.between(startTime, endTime); } - private static BooleanExpression volunteerEndDateTimeBefore(LocalDateTime now) { - return recruitBoard.recruitmentInfo.volunteerEndDateTime.before(now); + private static BooleanExpression volunteerEndDateTimeBetween(LocalDateTime startTime, + LocalDateTime endTime) { + return recruitBoard.recruitmentInfo.volunteerEndDateTime.between(startTime, endTime); } private OrderSpecifier[] toOrderSpecifiers(Sort sort) { diff --git a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java index 354682c7d..f94d7ef5c 100644 --- a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java +++ b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java @@ -27,12 +27,12 @@ public class RecruitBoardStatusUpdateScheduler { @Scheduled(cron = "0 0 0 * * ?") public void updateRecruitBoardStatusToClosed() { log.info("봉사 시작일에 해당하는 모집글 상태를 CLOSED로 변경하는 작업 시작"); - LocalDateTime startOfDay = LocalDate.now().atStartOfDay(); - LocalDateTime startOfNextDay = LocalDate.now().plusDays(1).atStartOfDay(); + LocalDateTime today = LocalDate.now().atStartOfDay(); + LocalDateTime tomorrow = today.plusDays(1); try { long updatedCount = recruitBoardRepository.updateRecruitingToClosedByStartDate( - startOfDay, startOfNextDay); + today, tomorrow); log.info("총 {}개의 모집글 상태를 CLOSED로 변경 완료", updatedCount); } catch (Exception e) { log.error("봉사 시작일에 해당하는 모집글 상태를 CLOSED로 변경하는 중 오류 발생", e); @@ -48,10 +48,11 @@ public void updateRecruitBoardStatusToClosed() { @Scheduled(cron = "0 0 0 * * ?") public void updateRecruitBoardStatusToCompleted() { log.info("봉사 종료일이 지난 모집글 상태를 COMPLETED로 변경하는 작업 시작"); - LocalDateTime now = LocalDateTime.now(); + LocalDateTime today = LocalDate.now().atStartOfDay(); + LocalDateTime yesterday = today.minusDays(1); try { - long updatedCount = recruitBoardRepository.updateClosedToCompletedByEndDate(now); + long updatedCount = recruitBoardRepository.updateClosedToCompletedByEndDate(yesterday, today); log.info("총 {}개의 모집글 상태를 COMPLETED로 변경 완료", updatedCount); } catch (Exception e) { log.error("봉사 종료일이 지난 모집글 상태를 COMPLETED로 변경하는 중 오류 발생", e); From b8a0fa97fdfe359d28f0d61ce8bedc9c262000fd Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Wed, 15 Jan 2025 16:19:18 +0900 Subject: [PATCH 22/25] =?UTF-8?q?test(recruit-board):=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A5=B4=EB=A7=81=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=20-=20betwe?= =?UTF-8?q?en()=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/RecruitBoardRepositoryImplTest.java | 12 ++++++------ .../RecruitBoardStatusUpdateSchedulerTest.java | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java index fca60d3a4..46af77cbc 100644 --- a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java @@ -392,17 +392,17 @@ void findAllByIds() { @Test void updateRecruitingToClosedByStartDate() { // given - LocalDateTime now = LocalDateTime.of(2024, 1, 1, 0, 0); // 2024-01-01 00:00:00 - LocalDateTime nextDay = now.plusDays(1); // 2024-01-02 00:00:00 - LocalDateTime startDateTime = now.plusHours(12); // 2024-01-01 12:00:00 + LocalDateTime today = LocalDateTime.of(2024, 1, 1, 0, 0); // 2024-01-01 00:00:00 + LocalDateTime startDateTime = today.plusHours(12); // 2024-01-01 12:00:00 LocalDateTime endDateTime = startDateTime.plusHours(2); // 2024-01-01 14:00:00 + LocalDateTime tomorrow = today.plusDays(1); // 2024-01-02 00:00:00 RecruitBoard boardOne = createRecruitBoard(startDateTime, endDateTime, RECRUITING); RecruitBoard boardTwo = createRecruitBoard(startDateTime, endDateTime, RECRUITING); recruitBoardRepository.saveAll(List.of(boardOne, boardTwo)); // when - long updateCnt = recruitBoardRepository.updateRecruitingToClosedByStartDate(now, nextDay); + long updateCnt = recruitBoardRepository.updateRecruitingToClosedByStartDate(today, tomorrow); em.clear(); // then @@ -427,7 +427,7 @@ void updateClosedToCompletedByEndDate() { recruitBoardRepository.saveAll(List.of(boardOne, boardTwo)); // when - long updateCnt = recruitBoardRepository.updateClosedToCompletedByEndDate(now); + long updateCnt = recruitBoardRepository.updateClosedToCompletedByEndDate(yesterday, now); em.clear(); // then @@ -520,7 +520,7 @@ public static RecruitBoard createRecruitBoard(UUID centerId, String title, .build(); } - public static RecruitBoard createRecruitBoard(LocalDateTime startTime, LocalDateTime endTime, + private static RecruitBoard createRecruitBoard(LocalDateTime startTime, LocalDateTime endTime, RecruitStatus status) { RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() diff --git a/src/test/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateSchedulerTest.java b/src/test/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateSchedulerTest.java index ad2bfbd1e..79b9d2d87 100644 --- a/src/test/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateSchedulerTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateSchedulerTest.java @@ -60,8 +60,8 @@ void updateRecruitBoardStatusToClosed() { @Test void updateRecruitBoardStatusToCompleted() { // given - LocalDateTime now = LocalDate.now().atStartOfDay(); // today 00:00:00 - LocalDateTime yesterday = now.minusDays(1); // yesterday 00:00:00 + LocalDateTime today = LocalDate.now().atStartOfDay(); // today 00:00:00 + LocalDateTime yesterday = today.minusDays(1); // yesterday 00:00:00 LocalDateTime startDateTime = yesterday.plusHours(12); // yesterday 12:00:00 LocalDateTime endDateTime = startDateTime.plusHours(2); // yesterday 14:00:00 @@ -80,7 +80,7 @@ void updateRecruitBoardStatusToCompleted() { assertThat(two.getRecruitStatus()).isEqualTo(COMPLETED); } - public static RecruitBoard createRecruitBoard(LocalDateTime startTime, LocalDateTime endTime, + private static RecruitBoard createRecruitBoard(LocalDateTime startTime, LocalDateTime endTime, RecruitStatus status) { RecruitmentInfo recruitmentInfo = RecruitmentInfo.builder() From 838b5584ecd8e0481840247b2ec17151196f2e75 Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Wed, 15 Jan 2025 16:23:14 +0900 Subject: [PATCH 23/25] =?UTF-8?q?refactor(recruit-board):=20=EC=8A=A4?= =?UTF-8?q?=EC=BC=80=EC=A4=84=20=EC=8B=9C=EA=B0=84=20=EC=83=81=EC=88=98?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheduler/RecruitBoardStatusUpdateScheduler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java index f94d7ef5c..ebfd34f1a 100644 --- a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java +++ b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java @@ -18,13 +18,14 @@ public class RecruitBoardStatusUpdateScheduler { private final RecruitBoardRepository recruitBoardRepository; + private final static String RECRUIT_BOARD_UPDATE_CRON = "0 0 0 * * ?"; @Retryable( retryFor = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000) ) - @Scheduled(cron = "0 0 0 * * ?") + @Scheduled(cron = RECRUIT_BOARD_UPDATE_CRON) public void updateRecruitBoardStatusToClosed() { log.info("봉사 시작일에 해당하는 모집글 상태를 CLOSED로 변경하는 작업 시작"); LocalDateTime today = LocalDate.now().atStartOfDay(); @@ -45,7 +46,7 @@ public void updateRecruitBoardStatusToClosed() { maxAttempts = 3, backoff = @Backoff(delay = 2000) ) - @Scheduled(cron = "0 0 0 * * ?") + @Scheduled(cron = RECRUIT_BOARD_UPDATE_CRON) public void updateRecruitBoardStatusToCompleted() { log.info("봉사 종료일이 지난 모집글 상태를 COMPLETED로 변경하는 작업 시작"); LocalDateTime today = LocalDate.now().atStartOfDay(); From 28e808bbc1c773625bf7115715dd0f059311342c Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Wed, 15 Jan 2025 16:27:51 +0900 Subject: [PATCH 24/25] =?UTF-8?q?refactor(recruit-board):=20sonar=20qube?= =?UTF-8?q?=20=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheduler/RecruitBoardStatusUpdateScheduler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java index ebfd34f1a..55ab63818 100644 --- a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java +++ b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java @@ -18,7 +18,7 @@ public class RecruitBoardStatusUpdateScheduler { private final RecruitBoardRepository recruitBoardRepository; - private final static String RECRUIT_BOARD_UPDATE_CRON = "0 0 0 * * ?"; + private static final String RECRUIT_BOARD_UPDATE_CRON = "0 0 0 * * ?"; @Retryable( retryFor = Exception.class, From ed81cf79ae0b99b189810a9f5ed8f751b3c1252b Mon Sep 17 00:00:00 2001 From: leebs0521 Date: Wed, 15 Jan 2025 16:56:47 +0900 Subject: [PATCH 25/25] =?UTF-8?q?refactor(recruit-board):=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=AA=85=20=EB=AA=85=ED=99=95=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recruitboard/repository/RecruitBoardRepository.java | 4 ++-- .../repository/RecruitBoardRepositoryImpl.java | 4 ++-- .../scheduler/RecruitBoardStatusUpdateScheduler.java | 4 ++-- .../repository/RecruitBoardRepositoryImplTest.java | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java index 991a10b49..abc0078cf 100644 --- a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java +++ b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java @@ -34,9 +34,9 @@ public interface RecruitBoardRepository { List findAll(); - long updateRecruitingToClosedByStartDate(LocalDateTime startTime, LocalDateTime endTime); + long updateStatusToClosedForDateRange(LocalDateTime startTime, LocalDateTime endTime); - long updateClosedToCompletedByEndDate(LocalDateTime startTime, LocalDateTime endTime); + long updateStatusToCompletedForDateRange(LocalDateTime startTime, LocalDateTime endTime); // Page findAllNearbyWithKeyword(RecruitBoardNearByCondition condition); // Page findByRecruitBoardsContaining(RecruitBoardSearchCondition condition); diff --git a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java index c56019155..d18e5a846 100644 --- a/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java +++ b/src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java @@ -210,7 +210,7 @@ public List findAll() { } @Override - public long updateRecruitingToClosedByStartDate(LocalDateTime startTime, + public long updateStatusToClosedForDateRange(LocalDateTime startTime, LocalDateTime endTime) { return queryFactory.update(recruitBoard) .set(recruitBoard.recruitStatus, CLOSED) @@ -223,7 +223,7 @@ public long updateRecruitingToClosedByStartDate(LocalDateTime startTime, } @Override - public long updateClosedToCompletedByEndDate(LocalDateTime startTime, LocalDateTime endTime) { + public long updateStatusToCompletedForDateRange(LocalDateTime startTime, LocalDateTime endTime) { return queryFactory.update(recruitBoard) .set(recruitBoard.recruitStatus, COMPLETED) .where( diff --git a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java index 55ab63818..deb8309e1 100644 --- a/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java +++ b/src/main/java/com/somemore/domains/recruitboard/scheduler/RecruitBoardStatusUpdateScheduler.java @@ -32,7 +32,7 @@ public void updateRecruitBoardStatusToClosed() { LocalDateTime tomorrow = today.plusDays(1); try { - long updatedCount = recruitBoardRepository.updateRecruitingToClosedByStartDate( + long updatedCount = recruitBoardRepository.updateStatusToClosedForDateRange( today, tomorrow); log.info("총 {}개의 모집글 상태를 CLOSED로 변경 완료", updatedCount); } catch (Exception e) { @@ -53,7 +53,7 @@ public void updateRecruitBoardStatusToCompleted() { LocalDateTime yesterday = today.minusDays(1); try { - long updatedCount = recruitBoardRepository.updateClosedToCompletedByEndDate(yesterday, today); + long updatedCount = recruitBoardRepository.updateStatusToCompletedForDateRange(yesterday, today); log.info("총 {}개의 모집글 상태를 COMPLETED로 변경 완료", updatedCount); } catch (Exception e) { log.error("봉사 종료일이 지난 모집글 상태를 COMPLETED로 변경하는 중 오류 발생", e); diff --git a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java index 46af77cbc..5df52ef81 100644 --- a/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java +++ b/src/test/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImplTest.java @@ -390,7 +390,7 @@ void findAllByIds() { @DisplayName("봉사 시작일 기준으로 모집중 상태 게시글을 모집 완료로 변경한다") @Test - void updateRecruitingToClosedByStartDate() { + void updateStatusToClosedForDateRange() { // given LocalDateTime today = LocalDateTime.of(2024, 1, 1, 0, 0); // 2024-01-01 00:00:00 LocalDateTime startDateTime = today.plusHours(12); // 2024-01-01 12:00:00 @@ -402,7 +402,7 @@ void updateRecruitingToClosedByStartDate() { recruitBoardRepository.saveAll(List.of(boardOne, boardTwo)); // when - long updateCnt = recruitBoardRepository.updateRecruitingToClosedByStartDate(today, tomorrow); + long updateCnt = recruitBoardRepository.updateStatusToClosedForDateRange(today, tomorrow); em.clear(); // then @@ -415,7 +415,7 @@ void updateRecruitingToClosedByStartDate() { @DisplayName("봉사 종료일 기준으로 모집완료 상태 게시글을 종료로 변경한다") @Test - void updateClosedToCompletedByEndDate() { + void updateStatusToCompletedForDateRange() { // given LocalDateTime now = LocalDateTime.of(2024, 1, 2, 0, 0); // 2024-01-02 00:00:00 LocalDateTime yesterday = now.minusDays(1); // 2024-01-01 00:00:00 @@ -427,7 +427,7 @@ void updateClosedToCompletedByEndDate() { recruitBoardRepository.saveAll(List.of(boardOne, boardTwo)); // when - long updateCnt = recruitBoardRepository.updateClosedToCompletedByEndDate(yesterday, now); + long updateCnt = recruitBoardRepository.updateStatusToCompletedForDateRange(yesterday, now); em.clear(); // then