diff --git a/src/main/java/com/somemore/recruitboard/domain/RecruitBoard.java b/src/main/java/com/somemore/recruitboard/domain/RecruitBoard.java index 1949ed9f6..14b3dad61 100644 --- a/src/main/java/com/somemore/recruitboard/domain/RecruitBoard.java +++ b/src/main/java/com/somemore/recruitboard/domain/RecruitBoard.java @@ -13,6 +13,7 @@ import jakarta.persistence.Id; import jakarta.persistence.Lob; import jakarta.persistence.Table; +import java.time.Duration; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.UUID; @@ -56,23 +57,26 @@ public class RecruitBoard extends BaseEntity { @Column(name = "recruit_status", nullable = false, length = 20) private RecruitStatus recruitStatus = RECRUITING; - @Column(name = "volunteer_date", nullable = false) - private LocalDateTime volunteerDate; + @Column(name = "volunteer_start_date_time", nullable = false) + private LocalDateTime volunteerStartDateTime; + + @Column(name = "volunteer_end_date_time", nullable = false) + private LocalDateTime volunteerEndDateTime; @Enumerated(value = STRING) @Column(name = "volunteer_type", nullable = false, length = 30) private VolunteerType volunteerType; - @Column(name = "volunteer_hours", nullable = false) - private LocalTime volunteerHours; - @Column(name = "admitted", nullable = false) private Boolean admitted; @Builder public RecruitBoard(UUID centerId, Long locationId, String title, String content, String region, - Integer recruitmentCount, String imgUrl, LocalDateTime volunteerDate, - VolunteerType volunteerType, LocalTime volunteerHours, Boolean admitted) { + Integer recruitmentCount, String imgUrl, LocalDateTime volunteerStartDateTime, + LocalDateTime volunteerEndDateTime, VolunteerType volunteerType, Boolean admitted) { + + validateVolunteerDateTime(volunteerStartDateTime, volunteerEndDateTime); + this.centerId = centerId; this.locationId = locationId; this.title = title; @@ -80,9 +84,24 @@ public RecruitBoard(UUID centerId, Long locationId, String title, String content this.region = region; this.recruitmentCount = recruitmentCount; this.imgUrl = imgUrl; - this.volunteerDate = volunteerDate; + this.volunteerStartDateTime = volunteerStartDateTime; + this.volunteerEndDateTime = volunteerEndDateTime; this.volunteerType = volunteerType; - this.volunteerHours = volunteerHours; this.admitted = admitted; } + + public LocalTime calculateVolunteerTime() { + Duration duration = Duration.between(volunteerStartDateTime, volunteerEndDateTime); + + long hours = duration.toHours(); + long minutes = duration.toMinutes() % 60; + + return LocalTime.of((int) hours, (int) minutes); + } + + private void validateVolunteerDateTime(LocalDateTime startDateTime, LocalDateTime endDateTime) { + if (endDateTime.isEqual(startDateTime) || endDateTime.isBefore(startDateTime)) { + throw new IllegalArgumentException("종료 시간은 시작 시간보다 이후여야 합니다."); + } + } } \ No newline at end of file diff --git a/src/main/java/com/somemore/recruitboard/dto/request/RecruitBoardCreateRequestDto.java b/src/main/java/com/somemore/recruitboard/dto/request/RecruitBoardCreateRequestDto.java index f419f9b5e..e047c5886 100644 --- a/src/main/java/com/somemore/recruitboard/dto/request/RecruitBoardCreateRequestDto.java +++ b/src/main/java/com/somemore/recruitboard/dto/request/RecruitBoardCreateRequestDto.java @@ -8,9 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; import java.time.LocalDateTime; -import java.time.LocalTime; import java.util.UUID; import lombok.Builder; @@ -29,24 +27,22 @@ public record RecruitBoardCreateRequestDto( @Schema(description = "예상 모집 인원", example = "4") @NotNull(message = "예상 모집 인원은 필수 값입니다.") Integer recruitmentCount, - @Schema(description = "봉사 일시", example = "2024-11-20T10:00:00") - @NotNull(message = "봉사 일시는 필수 값입니다.") - LocalDateTime volunteerDate, + @Schema(description = "봉사 시작 일시", example = "2024-11-20T10:00:00") + @NotNull(message = "봉사 시작 일시는 필수 값입니다.") + LocalDateTime volunteerStartDateTime, + @Schema(description = "봉사 종료 일시", example = "2024-11-20T12:00:00") + @NotNull(message = "봉사 종료 일시는 필수 값입니다.") + LocalDateTime volunteerEndDateTime, @Schema(description = "봉사 활동 유형", example = "ENVIRONMENTAL_PROTECTION") @NotNull(message = "봉사 활동 유형은 필수 값입니다.") VolunteerType volunteerType, - @Schema(description = "봉사 시간(시)", example = "1") - @Positive(message = "봉사 시간(시)은 1이상 이어야 합니다.") - Integer volunteerHours, - @Schema(description = "봉사 시간(분)", example = "30") - @Positive(message = "봉사 시간(분)은 1이상 이어야 합니다.") - Integer volunteerMinutes, @Schema(description = "봉사 시간 인정 여부", example = "true") @NotNull(message = "시간 인정 여부는 필수 값입니다.") Boolean admitted, @NotNull(message = "위치 정보는 필수 값입니다.") LocationCreateRequestDto location -){ +) { + public RecruitBoard toEntity(UUID centerId, Long locationId, String imgUrl) { return RecruitBoard.builder() .centerId(centerId) @@ -56,9 +52,9 @@ public RecruitBoard toEntity(UUID centerId, Long locationId, String imgUrl) { .region(region) .recruitmentCount(recruitmentCount) .imgUrl(imgUrl) - .volunteerDate(volunteerDate) + .volunteerStartDateTime(volunteerStartDateTime) + .volunteerEndDateTime(volunteerEndDateTime) .volunteerType(volunteerType) - .volunteerHours(LocalTime.of(volunteerHours, volunteerMinutes)) .admitted(admitted) .build(); } diff --git a/src/test/java/com/somemore/recruitboard/domain/RecruitBoardTest.java b/src/test/java/com/somemore/recruitboard/domain/RecruitBoardTest.java index 3e6939ea3..3ef77e328 100644 --- a/src/test/java/com/somemore/recruitboard/domain/RecruitBoardTest.java +++ b/src/test/java/com/somemore/recruitboard/domain/RecruitBoardTest.java @@ -3,18 +3,21 @@ import static com.somemore.recruitboard.domain.RecruitStatus.RECRUITING; import static com.somemore.recruitboard.domain.VolunteerType.OTHER; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.time.LocalDateTime; import java.time.LocalTime; 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; class RecruitBoardTest { @DisplayName("봉사 모집글 생성시 모집상태는 모집중이다") @Test - void CreateRecruitBoardWithDefaultStatus() { + void createRecruitBoardWithDefaultStatus() { // given RecruitBoard board = RecruitBoard.builder() .centerId(UUID.randomUUID()) @@ -24,9 +27,9 @@ void CreateRecruitBoardWithDefaultStatus() { .region("경기") .recruitmentCount(10) .imgUrl("https://image.domain.com/links") - .volunteerDate(LocalDateTime.now()) + .volunteerStartDateTime(LocalDateTime.now()) + .volunteerEndDateTime(LocalDateTime.now().plusHours(1)) .volunteerType(OTHER) - .volunteerHours(LocalTime.of(7, 30)) .admitted(true) .build(); @@ -36,4 +39,59 @@ void CreateRecruitBoardWithDefaultStatus() { // then assertThat(recruitStatus).isEqualTo(RECRUITING); } + + @DisplayName("봉사 종료 시간이 시작 시간과 같거나 빠르면, 봉사 모집글 생성 시 에러가 발생한다") + @ParameterizedTest + @ValueSource(longs = {0, -1}) + void createRecruitBoardWithInValidVolunteerTime(long secondsOffset) { + // given + LocalDateTime now = LocalDateTime.now(); + LocalDateTime endDateTime = now.plusSeconds(secondsOffset); + + // when & then + assertThatThrownBy( + () -> RecruitBoard.builder() + .centerId(UUID.randomUUID()) + .locationId(1L) + .title("봉사모집제목") + .content("봉사모집내용") + .region("경기") + .recruitmentCount(10) + .imgUrl("https://image.domain.com/links") + .volunteerStartDateTime(now) + .volunteerEndDateTime(endDateTime) + .volunteerType(VolunteerType.OTHER) + .admitted(true) + .build() + ).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("봉사 시간을 계산할 수 있다") + @Test + void testCalculateVolunteerTime() { + // given + int hours = 3; + LocalDateTime startDateTime = LocalDateTime.now(); + LocalDateTime endDateTime = startDateTime.plusHours(hours); + + RecruitBoard board = RecruitBoard.builder() + .centerId(UUID.randomUUID()) + .locationId(1L) + .title("봉사모집제목") + .content("봉사모집내용") + .region("경기") + .recruitmentCount(10) + .imgUrl("https://image.domain.com/links") + .volunteerStartDateTime(startDateTime) + .volunteerEndDateTime(endDateTime) + .volunteerType(OTHER) + .admitted(true) + .build(); + + // when + LocalTime volunteerTime = board.calculateVolunteerTime(); + + // then + assertThat(volunteerTime).isEqualTo(LocalTime.of(hours, 0)); + } } \ No newline at end of file diff --git a/src/test/java/com/somemore/recruitboard/service/command/CreateRecruitBoardServiceTest.java b/src/test/java/com/somemore/recruitboard/service/command/CreateRecruitBoardServiceTest.java index ba525cbcd..c97271866 100644 --- a/src/test/java/com/somemore/recruitboard/service/command/CreateRecruitBoardServiceTest.java +++ b/src/test/java/com/somemore/recruitboard/service/command/CreateRecruitBoardServiceTest.java @@ -11,7 +11,6 @@ import com.somemore.recruitboard.repository.RecruitBoardRepository; import java.math.BigDecimal; import java.time.LocalDateTime; -import java.time.LocalTime; import java.util.Optional; import java.util.UUID; import org.junit.jupiter.api.AfterEach; @@ -51,10 +50,9 @@ void createRecruitBoardWithDto() { .content("봉사 하실분을 모집합니다.
") .region("지역") .recruitmentCount(10) - .volunteerDate(LocalDateTime.now()) + .volunteerStartDateTime(LocalDateTime.now()) + .volunteerEndDateTime(LocalDateTime.now().plusHours(2)) .volunteerType(VolunteerType.OTHER) - .volunteerHours(1) - .volunteerMinutes(30) .admitted(true) .location(locationDto) .build(); @@ -72,7 +70,6 @@ void createRecruitBoardWithDto() { assertThat(recruitBoard.get().getId()).isEqualTo(saveId); assertThat(recruitBoard.get().getCenterId()).isEqualTo(centerId); assertThat(recruitBoard.get().getImgUrl()).isEqualTo(imgUrl); - assertThat(recruitBoard.get().getVolunteerHours()).isEqualTo(LocalTime.of(1, 30)); } } \ No newline at end of file