diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/DateSearchCondition.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/DateSearchCondition.java new file mode 100644 index 0000000..e796524 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/DateSearchCondition.java @@ -0,0 +1,6 @@ +package com.wypl.wyplcore.calendar.data; + +import java.time.LocalDate; + +public record DateSearchCondition(LocalDate startDate, LocalDate endDate) { +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/request/CalendarFindRequest.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/request/CalendarFindRequest.java new file mode 100644 index 0000000..6b4fe9b --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/request/CalendarFindRequest.java @@ -0,0 +1,16 @@ +package com.wypl.wyplcore.calendar.data.request; + +import java.time.LocalDate; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.wypl.wyplcore.schedule.data.CalendarType; + +public record CalendarFindRequest( + + @JsonProperty("calendar_type") + CalendarType calendarType, + + @JsonProperty("start_date") + LocalDate today +) { +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/response/CalendarSchedulesResponse.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/response/CalendarSchedulesResponse.java new file mode 100644 index 0000000..b3a4cee --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/data/response/CalendarSchedulesResponse.java @@ -0,0 +1,16 @@ +package com.wypl.wyplcore.calendar.data.response; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; + +public record CalendarSchedulesResponse( + + @JsonProperty("schedule_count") + int scheduleCount, + + @JsonProperty("schedules") + List schedules +) { +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/CalendarService.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/CalendarService.java new file mode 100644 index 0000000..3ba1a09 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/CalendarService.java @@ -0,0 +1,86 @@ +package com.wypl.wyplcore.calendar.service; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.wypl.googleoauthclient.domain.AuthMember; +import com.wypl.jpacalendardomain.calendar.domain.Calendar; +import com.wypl.jpacalendardomain.calendar.domain.MemberCalendar; +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.jpacalendardomain.calendar.repository.ScheduleRepository; +import com.wypl.jpamemberdomain.member.domain.Member; +import com.wypl.wyplcore.calendar.data.DateSearchCondition; +import com.wypl.wyplcore.calendar.data.request.CalendarFindRequest; +import com.wypl.wyplcore.calendar.data.response.CalendarSchedulesResponse; +import com.wypl.wyplcore.calendar.service.strategy.CalendarStrategy; +import com.wypl.wyplcore.schedule.data.CalendarType; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; +import com.wypl.wyplcore.schedule.service.repetition.RepetitionService; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class CalendarService { + + private final ScheduleRepository scheduleRepository; + + private final Map calendarStrategyMap; + + /** + * 캘린더 정보와 캘린더의 일정을 함께 조회한다. + * @param authMember : 인증된 사용자 정보 + * @param calendarId : 조회할 캘린더 ID + * @param calendarFindRequest : 캘린더 조회 조건 + * @return FindCalendarResponse + */ + @Transactional + public CalendarSchedulesResponse findCalendar(AuthMember authMember, long calendarId, + CalendarFindRequest calendarFindRequest) { + + Calendar foundCalendar = null; // FIXME: calendarId로 foundCalendar 엔티티 검증 필요. + MemberCalendar foundMemberCalendar = null; // FIXME: memberCalendar 엔티티 검증 필요. + Member foundMember = null; // FIXME: member 엔티티 검증 필요. + + DateSearchCondition dateSearchCondition = getDateSearchCondition(calendarFindRequest.today(), + calendarFindRequest.calendarType()); + + List schedules = scheduleRepository.findByCalendarIdAndBetweenStartDateAndEndDate(calendarId, + dateSearchCondition.startDate(), dateSearchCondition.endDate()); + + List scheduleFindResponses = new ArrayList<>(); + schedules.forEach(schedule -> { + scheduleFindResponses.addAll(getScheduleResponsesWithRepetition(schedule, dateSearchCondition.startDate(), + dateSearchCondition.endDate())); + }); + + return new CalendarSchedulesResponse(scheduleFindResponses.size(), scheduleFindResponses); + } + + /** + * RepetitionService를 통해 반복 일정을 가공하여 조회한다. + * @param schedule : 일정 정보 + * @param startDate : 조회 시작일 + * @param endDate : 조회 종료일 + * @return List : 일정 반복 정보를 통해 리스트 형태로 가공하여 반환된다. + */ + private List getScheduleResponsesWithRepetition(Schedule schedule, LocalDate startDate, + LocalDate endDate) { + return RepetitionService.getScheduleResponses(schedule, startDate, endDate); + } + + /** + * CalendarType에 따라 DateSearchCondition을 반환한다. + * @param today : 조회 기준일 + * @param calendarType : 조회할 캘린더 타입 + * @return DateSearchCondition : DateSearchCondition 객체를 반환한다. + */ + private DateSearchCondition getDateSearchCondition(LocalDate today, CalendarType calendarType) { + return calendarStrategyMap.get(calendarType).getDateSearchCondition(today); + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/CalendarServiceUtil.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/CalendarServiceUtil.java new file mode 100644 index 0000000..9f2c0a8 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/CalendarServiceUtil.java @@ -0,0 +1,15 @@ +package com.wypl.wyplcore.calendar.service; + +import java.time.LocalDate; + +public class CalendarServiceUtil { + + public static LocalDate getMaxDate(LocalDate date1, LocalDate date2) { + return date1.isBefore(date2) ? date2 : date1; + } + + public static LocalDate getMinDate(LocalDate date1, LocalDate date2) { + return date1.isBefore(date2) ? date1 : date2; + } + +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategy.java new file mode 100644 index 0000000..34ecef0 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategy.java @@ -0,0 +1,13 @@ +package com.wypl.wyplcore.calendar.service.strategy; + +import java.time.LocalDate; + +import com.wypl.wyplcore.calendar.data.DateSearchCondition; +import com.wypl.wyplcore.schedule.data.CalendarType; + +public interface CalendarStrategy { + + CalendarType getCalendarType(); + + DateSearchCondition getDateSearchCondition(LocalDate today); +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategyConfig.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategyConfig.java new file mode 100644 index 0000000..2454fc5 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategyConfig.java @@ -0,0 +1,29 @@ +package com.wypl.wyplcore.calendar.service.strategy; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.wypl.wyplcore.schedule.data.CalendarType; + +@Configuration +public class CalendarStrategyConfig { + + private final Map calendarStrategyMap = new HashMap<>(); + + @Autowired + public CalendarStrategyConfig(List calendarStrategies) { + calendarStrategies.forEach(calendarStrategy -> { + calendarStrategyMap.put(calendarStrategy.getCalendarType(), calendarStrategy); + }); + } + + @Bean + public Map calendarStrategyMap() { + return calendarStrategyMap; + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/DayCalendarStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/DayCalendarStrategy.java new file mode 100644 index 0000000..8e0b5a2 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/DayCalendarStrategy.java @@ -0,0 +1,26 @@ +package com.wypl.wyplcore.calendar.service.strategy; + +import java.time.LocalDate; + +import org.springframework.stereotype.Component; + +import com.wypl.wyplcore.calendar.data.DateSearchCondition; +import com.wypl.wyplcore.schedule.data.CalendarType; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class DayCalendarStrategy implements CalendarStrategy { + + @Override + public CalendarType getCalendarType() { + return CalendarType.DAY; + } + + @Override + public DateSearchCondition getDateSearchCondition(LocalDate today) { + return new DateSearchCondition(today, today); + } + +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/MonthCalendarStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/MonthCalendarStrategy.java new file mode 100644 index 0000000..be55b1e --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/MonthCalendarStrategy.java @@ -0,0 +1,28 @@ +package com.wypl.wyplcore.calendar.service.strategy; + +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; + +import org.springframework.stereotype.Component; + +import com.wypl.wyplcore.calendar.data.DateSearchCondition; +import com.wypl.wyplcore.schedule.data.CalendarType; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class MonthCalendarStrategy implements CalendarStrategy { + + @Override + public CalendarType getCalendarType() { + return CalendarType.MONTH; + } + + @Override + public DateSearchCondition getDateSearchCondition(LocalDate today) { + LocalDate searchStartDate = today.withDayOfMonth(1); + LocalDate searchEndDate = today.with(TemporalAdjusters.lastDayOfMonth()); + return new DateSearchCondition(searchStartDate, searchEndDate); + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/WeekCalendarStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/WeekCalendarStrategy.java new file mode 100644 index 0000000..890b92a --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/calendar/service/strategy/WeekCalendarStrategy.java @@ -0,0 +1,29 @@ +package com.wypl.wyplcore.calendar.service.strategy; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; + +import org.springframework.stereotype.Component; + +import com.wypl.wyplcore.calendar.data.DateSearchCondition; +import com.wypl.wyplcore.schedule.data.CalendarType; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class WeekCalendarStrategy implements CalendarStrategy { + + @Override + public CalendarType getCalendarType() { + return CalendarType.WEEK; + } + + @Override + public DateSearchCondition getDateSearchCondition(LocalDate today) { + LocalDate searchStartDate = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + LocalDate searchEndDate = today.plusDays(6); + return new DateSearchCondition(searchStartDate, searchEndDate); + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/exception/ReviewErrorCode.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/exception/ReviewErrorCode.java index 4b28329..c993f3c 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/exception/ReviewErrorCode.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/exception/ReviewErrorCode.java @@ -8,8 +8,7 @@ public enum ReviewErrorCode implements ServerErrorCode { EMPTY_CONTENTS(500, "REVIEW_001", "작성된 내용이 없습니다."), INVALID_TITLE(500, "REVIEW_002", "제목의 길이가 올바르지 않습니다."), - NO_SUCH_REVIEW(500, "REVIEW_003", "존재하지 않는 회고입니다.") - ; + NO_SUCH_REVIEW(500, "REVIEW_003", "존재하지 않는 회고입니다."); private final int statusCode; private final String errorCode; diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/service/ReviewServiceImpl.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/service/ReviewServiceImpl.java index 9de4c01..44dd1b9 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/service/ReviewServiceImpl.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/service/ReviewServiceImpl.java @@ -66,7 +66,7 @@ public ReviewIdResponse createReview(long memberId, ReviewCreateRequest reviewCr public ReviewIdResponse updateReview(long memberId, long reviewId, ReviewUpdateRequest reviewUpdateRequest) { validateReviewContents(reviewUpdateRequest.contents(), reviewUpdateRequest.title()); - Member member = null; // Todo : 조회 + Member member = null; // Todo : 조회 Review review = ReviewUtils.findByReviewIdAndMember(reviewRepository, reviewId, member); review.updateTitle(reviewUpdateRequest.title()); @@ -82,7 +82,7 @@ public ReviewIdResponse updateReview(long memberId, long reviewId, ReviewUpdateR @Override @Transactional public ReviewIdResponse deleteReview(long memberId, long reviewId) { - Member member = null; // Todo : 조회 + Member member = null; // Todo : 조회 Review review = ReviewUtils.findByReviewIdAndMember(reviewRepository, reviewId, member); ReviewContents reviewContents = reviewContentsRepository.findByReviewIdAndDeletedAtNull(review.getReviewId()); @@ -97,7 +97,7 @@ public ReviewIdResponse deleteReview(long memberId, long reviewId) { @Override public ReviewDetailResponse getDetailReview(long memberId, long reviewId) { - Member member = null; // Todo : 조회 + Member member = null; // Todo : 조회 Review review = ReviewUtils.findByReviewIdAndMember(reviewRepository, reviewId, member); Schedule schedule = review.getSchedule(); @@ -165,8 +165,8 @@ public ReviewListResponse getReviewsByScheduleId(long memberId, long scheduleId, // MemberSchedule memberSchedule = memberScheduleService.getMemberScheduleByMemberAndSchedule(memberId, // ScheduleServiceUtils.findById(scheduleRepository, scheduleId)); - Member member = null; // Todo : 조회 - Schedule schedule = null; // Todo : 조회 + Member member = null; // Todo : 조회 + Schedule schedule = null; // Todo : 조회 List reviews = switch (reviewType) { case NEWEST -> { diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/controller/ScheduleController.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/controller/ScheduleController.java index 369edcf..fae49c5 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/controller/ScheduleController.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/controller/ScheduleController.java @@ -19,11 +19,12 @@ @RequestMapping("/schedule/v2/schedules") public class ScheduleController { - private final ScheduleService scheduleService; + private final ScheduleService scheduleService; - @PostMapping - public WyplResponseEntity addSchedule(@Authenticated AuthMember authMember, @RequestBody ScheduleCreateRequest scheduleCreateRequest) { - ScheduleInfoCreateResponse response = scheduleService.createSchedule(authMember, scheduleCreateRequest); - return WyplResponseEntity.created(response, "일정이 생성됐습니다."); - } + @PostMapping + public WyplResponseEntity addSchedule(@Authenticated AuthMember authMember, + @RequestBody ScheduleCreateRequest scheduleCreateRequest) { + ScheduleInfoCreateResponse response = scheduleService.createSchedule(authMember, scheduleCreateRequest); + return WyplResponseEntity.created(response, "일정이 생성됐습니다."); + } } diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/CalendarType.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/CalendarType.java new file mode 100644 index 0000000..da3a38f --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/CalendarType.java @@ -0,0 +1,5 @@ +package com.wypl.wyplcore.schedule.data; + +public enum CalendarType { + DAY, WEEK, MONTH +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/request/ScheduleCreateRequest.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/request/ScheduleCreateRequest.java index a160785..b2cc7a3 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/request/ScheduleCreateRequest.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/request/ScheduleCreateRequest.java @@ -1,86 +1,87 @@ package com.wypl.wyplcore.schedule.data.request; +import java.time.LocalDate; +import java.time.LocalDateTime; + import com.fasterxml.jackson.annotation.JsonProperty; import com.wypl.jpacalendardomain.calendar.data.ConvertibleSchedule; import com.wypl.jpacalendardomain.calendar.data.RepetitionCycle; -import lombok.Builder; -import java.time.LocalDate; -import java.time.LocalDateTime; +import lombok.Builder; @Builder public record ScheduleCreateRequest( - @JsonProperty("calendar_id") - long calenderId, + @JsonProperty("calendar_id") + long calenderId, - String title, + String title, - String description, + String description, - @JsonProperty("start_datetime") - LocalDateTime startDateTime, + @JsonProperty("start_datetime") + LocalDateTime startDateTime, - @JsonProperty("end_datetime") - LocalDateTime endDateTime, + @JsonProperty("end_datetime") + LocalDateTime endDateTime, - @JsonProperty("repetition_cycle") - RepetitionCycle repetitionCycle, + @JsonProperty("repetition_cycle") + RepetitionCycle repetitionCycle, - @JsonProperty("day_of_week") - int dayOfWeek, + @JsonProperty("day_of_week") + int dayOfWeek, - @JsonProperty("week_interval") - Integer weekInterval, + @JsonProperty("week_interval") + Integer weekInterval, - @JsonProperty("repetition_start_date") - LocalDate repetitionStartDate, + @JsonProperty("repetition_start_date") + LocalDate repetitionStartDate, - @JsonProperty("repetition_end_date") - LocalDate repetitionEndDate + @JsonProperty("repetition_end_date") + LocalDate repetitionEndDate ) implements ConvertibleSchedule { - @Override - public String getTitle() { - return this.title; - } - - @Override - public String getDescription() { - return this.description; - } - - @Override - public LocalDateTime getStartDateTime() { - return this.startDateTime; - } - - @Override - public LocalDateTime getEndDateTime() { - return this.endDateTime; - } - - @Override - public LocalDate getRepetitionStartDate() { - return this.repetitionStartDate; - } - - @Override - public LocalDate getRepetitionEndDate() { - return this.repetitionEndDate; - } - - @Override - public RepetitionCycle getRepetitionCycle() { - return this.repetitionCycle; - } - - @Override - public Integer getDayOfWeek() { - return this.dayOfWeek; - } - - @Override - public Integer getWeekInterval() { - return this.weekInterval; - } + @Override + public String getTitle() { + return this.title; + } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public LocalDateTime getStartDateTime() { + return this.startDateTime; + } + + @Override + public LocalDateTime getEndDateTime() { + return this.endDateTime; + } + + @Override + public LocalDate getRepetitionStartDate() { + return this.repetitionStartDate; + } + + @Override + public LocalDate getRepetitionEndDate() { + return this.repetitionEndDate; + } + + @Override + public RepetitionCycle getRepetitionCycle() { + return this.repetitionCycle; + } + + @Override + public Integer getDayOfWeek() { + return this.dayOfWeek; + } + + @Override + public Integer getWeekInterval() { + return this.weekInterval; + } } \ No newline at end of file diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/response/ScheduleFindResponse.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/response/ScheduleFindResponse.java new file mode 100644 index 0000000..b74bace --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/response/ScheduleFindResponse.java @@ -0,0 +1,36 @@ +package com.wypl.wyplcore.schedule.data.response; + +import java.time.LocalDateTime; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.wypl.jpacalendardomain.calendar.domain.Schedule; + +public record ScheduleFindResponse( + + @JsonProperty("schedule_id") + long scheduleId, + + @JsonProperty("title") + String title, + + @JsonProperty("description") + String description, + + @JsonProperty("start_date_time") + LocalDateTime startDateTime, + + @JsonProperty("end_date_time") + LocalDateTime endDateTime + +) { + + public static ScheduleFindResponse of(Schedule schedule, LocalDateTime startDateTime, LocalDateTime endDateTime) { + return new ScheduleFindResponse( + schedule.getId(), + schedule.getTitle(), + schedule.getDescription(), + startDateTime, + endDateTime + ); + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/response/ScheduleInfoCreateResponse.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/response/ScheduleInfoCreateResponse.java index 85acdab..d28234a 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/response/ScheduleInfoCreateResponse.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/data/response/ScheduleInfoCreateResponse.java @@ -2,13 +2,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; -public record ScheduleInfoCreateResponse ( +public record ScheduleInfoCreateResponse( - @JsonProperty("calendar_id") - Long calendarId, + @JsonProperty("calendar_id") + Long calendarId, - @JsonProperty("schedule_info_id") - Long scheduleInfoId + @JsonProperty("schedule_info_id") + Long scheduleInfoId -){ +) { } diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/ScheduleService.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/ScheduleService.java index 35a669e..1df980f 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/ScheduleService.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/ScheduleService.java @@ -17,27 +17,33 @@ import lombok.RequiredArgsConstructor; - @Service @RequiredArgsConstructor public class ScheduleService { - private final ScheduleRepository scheduleRepository; - private final ScheduleInfoRepository scheduleInfoRepository; + private final ScheduleRepository scheduleRepository; + private final ScheduleInfoRepository scheduleInfoRepository; - @Transactional - public ScheduleInfoCreateResponse createSchedule(AuthMember authMember, ScheduleCreateRequest scheduleCreateRequest) { + /** + * Schedule을 생성한다. + * @param authMember + * @param scheduleCreateRequest + * @return ScheduleInfoCreateResponse + */ + @Transactional + public ScheduleInfoCreateResponse createSchedule(AuthMember authMember, + ScheduleCreateRequest scheduleCreateRequest) { - Calendar foundCalendar = null; // FIXME: scheduleInfoRequest의 calendarId로 찾는다. foundCalendar 엔티티 검증 필요. - Member foundMember = null; // FIXME: member 엔티티 검증 필요. + Calendar foundCalendar = null; // FIXME: scheduleInfoRequest의 calendarId로 찾는다. foundCalendar 엔티티 검증 필요. + Member foundMember = null; // FIXME: member 엔티티 검증 필요. - ScheduleInfo scheduleInfo = ScheduleInfoMapper.toJpaScheduleInfo(foundCalendar, authMember.id()); - Schedule schedule = ScheduleMapper.toJpaSchedule(scheduleCreateRequest, scheduleInfo); + ScheduleInfo scheduleInfo = ScheduleInfoMapper.toJpaScheduleInfo(foundCalendar, authMember.id()); + Schedule schedule = ScheduleMapper.toJpaSchedule(scheduleCreateRequest, scheduleInfo); - ScheduleInfo savedScheduleInfo = scheduleInfoRepository.save(scheduleInfo); - Schedule savedSchedule = scheduleRepository.save(schedule); + ScheduleInfo savedScheduleInfo = scheduleInfoRepository.save(scheduleInfo); + Schedule savedSchedule = scheduleRepository.save(schedule); - return new ScheduleInfoCreateResponse(foundCalendar.getId(), savedScheduleInfo.getId()); - } + return new ScheduleInfoCreateResponse(foundCalendar.getId(), savedScheduleInfo.getId()); + } } diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/RepetitionService.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/RepetitionService.java new file mode 100644 index 0000000..4b8fb1b --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/RepetitionService.java @@ -0,0 +1,25 @@ +package com.wypl.wyplcore.schedule.service.repetition; + +import java.time.LocalDate; +import java.util.List; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; +import com.wypl.wyplcore.schedule.service.repetition.strategy.RepetitionStrategy; + +public class RepetitionService { + + /** + * Schedule의 반복 일정에 따라 ScheduleFindResponse를 조회한다. + * @param schedule + * @param searchStartDate + * @param searchEndDate + * @return List + */ + public static List getScheduleResponses(Schedule schedule, LocalDate searchStartDate, + LocalDate searchEndDate) { + RepetitionStrategy repetitionStrategy = RepetitionStrategyFactory.getRepetitionStrategy( + schedule.getRepetitionCycle()); + return repetitionStrategy.getScheduleResponses(schedule, searchStartDate, searchEndDate); + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/RepetitionStrategyFactory.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/RepetitionStrategyFactory.java new file mode 100644 index 0000000..60b6a32 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/RepetitionStrategyFactory.java @@ -0,0 +1,37 @@ +package com.wypl.wyplcore.schedule.service.repetition; + +import java.util.HashMap; +import java.util.Map; + +import com.wypl.jpacalendardomain.calendar.data.RepetitionCycle; +import com.wypl.wyplcore.schedule.service.repetition.strategy.DayRepetitionStrategy; +import com.wypl.wyplcore.schedule.service.repetition.strategy.MonthRepetitionStrategy; +import com.wypl.wyplcore.schedule.service.repetition.strategy.RepetitionStrategy; +import com.wypl.wyplcore.schedule.service.repetition.strategy.WeekRepetitionStrategy; +import com.wypl.wyplcore.schedule.service.repetition.strategy.YearRepetitionStrategy; + +public class RepetitionStrategyFactory { + + private static RepetitionStrategyFactory instance; + + private final Map map = new HashMap<>(); + + private RepetitionStrategyFactory() { + map.put(RepetitionCycle.DAY, new DayRepetitionStrategy()); + map.put(RepetitionCycle.WEEK, new WeekRepetitionStrategy()); + map.put(RepetitionCycle.MONTH, new MonthRepetitionStrategy()); + map.put(RepetitionCycle.YEAR, new YearRepetitionStrategy()); + } + + /** + * RepetitionCycle에 따른 RepetitionStrategy를 반환한다. + * @param repetitionCycle + * @return RepetitionStrategy + */ + public static RepetitionStrategy getRepetitionStrategy(RepetitionCycle repetitionCycle) { + if (instance == null) { + instance = new RepetitionStrategyFactory(); + } + return instance.map.get(repetitionCycle); + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/DayRepetitionStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/DayRepetitionStrategy.java new file mode 100644 index 0000000..68a1971 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/DayRepetitionStrategy.java @@ -0,0 +1,38 @@ +package com.wypl.wyplcore.schedule.service.repetition.strategy; + +import static com.wypl.wyplcore.calendar.service.CalendarServiceUtil.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; + +public class DayRepetitionStrategy implements RepetitionStrategy { + + /** + * RepetitionCycle이 Day일 때 Schedule의 반복 일정을 조회한다. + * @param schedule + * @param searchStartDate + * @param searchEndDate + * @return List + */ + @Override + public List getScheduleResponses(Schedule schedule, LocalDate searchStartDate, + LocalDate searchEndDate) { + + List responses = new ArrayList<>(); + + searchStartDate = getMaxDate(searchStartDate, schedule.getRepetitionStartDate()); + searchEndDate = getMinDate(searchEndDate, schedule.getRepetitionEndDate()); + + for (; !searchStartDate.isAfter(searchEndDate); searchStartDate = searchStartDate.plusDays(1)) { + LocalDateTime startDateTime = LocalDateTime.of(searchStartDate, schedule.getStartDateTime().toLocalTime()); + LocalDateTime endDateTime = LocalDateTime.of(searchStartDate, schedule.getEndDateTime().toLocalTime()); + responses.add(ScheduleFindResponse.of(schedule, startDateTime, endDateTime)); + } + return responses; + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/MonthRepetitionStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/MonthRepetitionStrategy.java new file mode 100644 index 0000000..b6ceda9 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/MonthRepetitionStrategy.java @@ -0,0 +1,48 @@ +package com.wypl.wyplcore.schedule.service.repetition.strategy; + +import static com.wypl.wyplcore.calendar.service.CalendarServiceUtil.*; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; + +public class MonthRepetitionStrategy implements RepetitionStrategy { + + /** + * RepetitionCycle이 Month일 때 Schedule의 반복 일정을 조회한다. + * @param schedule + * @param searchStartDate + * @param searchEndDate + * @return List + */ + @Override + public List getScheduleResponses(Schedule schedule, LocalDate searchStartDate, + LocalDate searchEndDate) { + + List responses = new ArrayList<>(); + searchStartDate = getMaxDate(searchStartDate, schedule.getRepetitionStartDate()); + searchEndDate = getMinDate(searchEndDate, schedule.getRepetitionEndDate()); + + // 끝나는 날짜 + int endDayOfMonth = schedule.getEndDateTime().getDayOfMonth(); + + // 탐색할 시작 일시와 끝 일시 설정 + LocalDateTime searchEndDateTime = LocalDateTime.of(searchStartDate.withDayOfMonth(endDayOfMonth), + schedule.getEndDateTime().toLocalTime()); + Duration duration = Duration.between(schedule.getStartDateTime(), schedule.getEndDateTime()); + LocalDateTime searchStartDateTime = searchEndDateTime.minus(duration); + + for (LocalDate date = searchStartDateTime.toLocalDate(); !date.isAfter(searchEndDate); date = date.plusMonths( + 1)) { + LocalDateTime startDateTime = LocalDateTime.of(date, schedule.getStartDateTime().toLocalTime()); + LocalDateTime endDateTime = startDateTime.plus(duration); + responses.add(ScheduleFindResponse.of(schedule, startDateTime, endDateTime)); + } + return responses; + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/RepetitionStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/RepetitionStrategy.java new file mode 100644 index 0000000..f54a5f0 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/RepetitionStrategy.java @@ -0,0 +1,14 @@ +package com.wypl.wyplcore.schedule.service.repetition.strategy; + +import java.time.LocalDate; +import java.util.List; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; + +public interface RepetitionStrategy { + + List getScheduleResponses(Schedule schedule, LocalDate searchStartDate, + LocalDate searchEndDate); + +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/WeekRepetitionStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/WeekRepetitionStrategy.java new file mode 100644 index 0000000..f16d84f --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/WeekRepetitionStrategy.java @@ -0,0 +1,128 @@ +package com.wypl.wyplcore.schedule.service.repetition.strategy; + +import static com.wypl.wyplcore.calendar.service.CalendarServiceUtil.*; + +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.TemporalAdjusters; +import java.util.ArrayList; +import java.util.List; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; + +public class WeekRepetitionStrategy implements RepetitionStrategy { + + /** + * 검색 조건에는 포함되지만, 검색이 시작된 날짜보다 이전에 시작한 Schedule 반영 + * @param searchStartDate + * @param schedule + * @return searchStartDate + * Todo: 일정이 일주일 이상 지속될 경우에는 처리 불가, 생각해 보기 + */ + private static LocalDate resetForBeforeStarted(LocalDate searchStartDate, Schedule schedule) { + + // 끝나는 요일 찾기 + DayOfWeek endDayOfWeek = schedule.getEndDateTime().getDayOfWeek(); + Duration duration = Duration.between(schedule.getStartDateTime(), schedule.getEndDateTime()); + + // 탐색을 시작할 일자 설정 + LocalDateTime endDateTimeForSearch = searchStartDate.with(TemporalAdjusters.nextOrSame(endDayOfWeek)) + .atTime(schedule.getEndDateTime().toLocalTime()); + + return endDateTimeForSearch.minus(duration).toLocalDate(); + } + + /** + * 검색 시작일자에 WeekInterval을 적용 + * @param searchStartDate + * @param weekInterval + * @param schedule + * @return searchStartDate + */ + public static LocalDate getNearestDateUsingWeekInterval(LocalDate searchStartDate, Integer weekInterval, + Schedule schedule) { + + // searchStartDate를 해당 주의 월요일로 설정 + LocalDateTime searchStartMonday = LocalDateTime.of( + searchStartDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)), LocalTime.of(0, 0)); + LocalDateTime scheduleStartMonday = LocalDateTime.of( + schedule.getRepetitionStartDate().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)), + LocalTime.of(0, 0)); + + int gapOfWeek = (int)Duration.between(scheduleStartMonday, searchStartMonday).toDays() / 7; + + if (gapOfWeek % weekInterval == 0) + return searchStartDate; + + int addWeek = (gapOfWeek / weekInterval + 1) * weekInterval - gapOfWeek; + + return searchStartDate.plusWeeks(addWeek); + } + + /** + * repetitionDayOfWeek(반복요일)에 해당하는 요일이 선택되었는지 확인 + * @param repetitionDayOfWeek of Schedule + * @param dayOfWeek (1: 월요일, 2: 화요일, 3: 수요일, 4: 목요일, 5: 금요일, 6: 토요일, 7: 일요일) + * @return boolean + */ + static boolean isSelectedDayOfWeek(int repetitionDayOfWeek, int dayOfWeek) { + return (repetitionDayOfWeek & (1 << dayOfWeek)) != 0; + } + + /** + * RepetitionCycle이 Week일 때 Schedule의 반복 일정을 조회한다. + * @param schedule + * @param searchStartDate + * @param searchEndDate + * @return List + */ + @Override + public List getScheduleResponses(Schedule schedule, LocalDate searchStartDate, + LocalDate searchEndDate) { + + // 검색할 시작일자와 끝일자 설정 + searchEndDate = getMinDate(searchEndDate, schedule.getRepetitionEndDate()); + searchStartDate = getMaxDate(searchStartDate, schedule.getRepetitionStartDate()); + + // 반복 주에 포함되도록 가공 + searchStartDate = getNearestDateUsingWeekInterval(searchStartDate, schedule.getWeekInterval(), schedule); + List responses = new ArrayList<>(); + + if (!schedule.existsDayOfWeek()) { // 반복 요일을 설정하지 않았을 경우 + + searchStartDate = resetForBeforeStarted(searchStartDate, schedule); + Duration duration = Duration.between(schedule.getStartDateTime(), schedule.getEndDateTime()); + + // 설정한 weekInterval 만큼씩 증가하면서 endDate까지 반복 + for (LocalDate date = searchStartDate; !date.isAfter(searchEndDate); date = date.plusWeeks( + schedule.getWeekInterval())) { + LocalDateTime startDateTime = LocalDateTime.of(date, schedule.getStartDateTime().toLocalTime()); + LocalDateTime endDateTime = startDateTime.plus(duration); + responses.add(ScheduleFindResponse.of(schedule, startDateTime, endDateTime)); + } + return responses; + } + // 반복 요일을 설정했을 경우 + int repetitionDayOfWeek = schedule.getDayOfWeek(); + + for (int dayOfWeek = 1; dayOfWeek <= 7; dayOfWeek++) { // 요일마다 처리 + if (isSelectedDayOfWeek(repetitionDayOfWeek, dayOfWeek)) { + + // Find nearest day by dayOfWeek and increase weekInterval + LocalDate date = getMaxDate(searchStartDate, schedule.getRepetitionStartDate()).with( + TemporalAdjusters.nextOrSame(DayOfWeek.of(dayOfWeek))); + + for (; !date.isAfter(searchEndDate); date = date.plusWeeks(schedule.getWeekInterval())) { + responses.add(ScheduleFindResponse + .of(schedule, LocalDateTime.of(date, schedule.getStartDateTime().toLocalTime()), + LocalDateTime.of(date, schedule.getEndDateTime().toLocalTime()))); + } + } + } + return responses; + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/YearRepetitionStrategy.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/YearRepetitionStrategy.java new file mode 100644 index 0000000..343530c --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/repetition/strategy/YearRepetitionStrategy.java @@ -0,0 +1,62 @@ +package com.wypl.wyplcore.schedule.service.repetition.strategy; + +import static com.wypl.wyplcore.calendar.service.CalendarServiceUtil.*; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; + +@Service +public class YearRepetitionStrategy implements RepetitionStrategy { + + /** + * searchStartDate 검색 조건에 부합하는 가장 가까운 일정의 시작일자를 반환 + * @param schedule + * @param searchStartDate + * @return LocalDateTime + */ + private static LocalDateTime getNearestStartDateTime(Schedule schedule, LocalDate searchStartDate) { + LocalDateTime nearestEndDateTime = LocalDateTime.of( + searchStartDate.withDayOfYear(schedule.getEndDateTime().getDayOfYear()), + schedule.getEndDateTime().toLocalTime()); + LocalDateTime nearestStartDateTime = nearestEndDateTime.minus( + Duration.between(schedule.getStartDateTime(), schedule.getEndDateTime())); + return nearestStartDateTime; + } + + /** + * RepetitionCycle이 Year일 때 Schedule의 반복 일정을 조회한다. + * @param schedule + * @param searchStartDate + * @param searchEndDate + * @return List + */ + @Override + public List getScheduleResponses(Schedule schedule, LocalDate searchStartDate, + LocalDate searchEndDate) { + + List responses = new ArrayList<>(); + + searchStartDate = getMaxDate(searchStartDate, schedule.getRepetitionStartDate()); + searchEndDate = getMinDate(searchEndDate, schedule.getRepetitionEndDate()); + + LocalDateTime nearestStartDateTime = getNearestStartDateTime(schedule, searchStartDate); + + for (LocalDate date = nearestStartDateTime.toLocalDate(); !date.isAfter(searchEndDate); date = date.plusYears( + 1)) { + LocalDateTime startDateTime = LocalDateTime.of(date, schedule.getStartDateTime().toLocalTime()); + LocalDateTime endDateTime = startDateTime.plus( + Duration.between(schedule.getStartDateTime(), schedule.getEndDateTime())); + responses.add(ScheduleFindResponse.of(schedule, startDateTime, endDateTime)); + } + return responses; + } + +} diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/CalendarFixture.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/CalendarFixture.java new file mode 100644 index 0000000..597dd8a --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/CalendarFixture.java @@ -0,0 +1,35 @@ +package com.wypl.wyplcore; + +import com.wypl.jpacalendardomain.calendar.domain.Calendar; + +import lombok.Getter; + +@Getter +public enum CalendarFixture { + + PRIVATE_CALENDAR("개인 달력", "개인 달력입니다.", 1L, false), + GROUP_CALENDAR1("와플 그룹 달력", "와플 화이팅", 2L, true), + GROUP_CALENDAR2("CS 스터디 그룹 달력", "CS 재밋다!", 3L, true); + + private final String name; + private final String description; + private final Long ownerId; + private final Boolean isShared; + + CalendarFixture(String name, String description, Long ownerId, Boolean isShared) { + this.name = name; + this.description = description; + this.ownerId = ownerId; + this.isShared = isShared; + } + + public Calendar toEntity() { + return Calendar.builder() + .name(this.name) + .description(this.description) + .ownerId(this.ownerId) + .isShared(this.isShared) + .build(); + } + +} \ No newline at end of file diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/ScheduleFixture.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/ScheduleFixture.java new file mode 100644 index 0000000..69c7f9a --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/ScheduleFixture.java @@ -0,0 +1,135 @@ +package com.wypl.wyplcore; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +import com.wypl.jpacalendardomain.calendar.data.RepetitionCycle; +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.jpacalendardomain.calendar.domain.ScheduleInfo; + +import lombok.Getter; + +@Getter +public enum ScheduleFixture { + + DAILY_SCHEDULE( + "일일 일정 1", + "매일 오전 9시에서 오전 10시까지 반복되는 일정입니다.", + LocalDateTime.of(2024, 10, 20, 9, 0), + LocalDateTime.of(2024, 10, 20, 10, 0), + LocalDate.of(2024, 10, 20), + LocalDate.of(2024, 12, 31), + RepetitionCycle.DAY, + null, + null + ), + DAILY_SCHEDULE_2( + "일일 일정 2", + "매일 오후 한시에서 오후 2시 반까지 반복되는 일정입니다.", + LocalDateTime.of(2024, 10, 20, 13, 0), + LocalDateTime.of(2024, 10, 20, 14, 30), + LocalDate.of(2024, 10, 20), + LocalDate.of(2025, 12, 31), + RepetitionCycle.DAY, + null, + null + ), + WEEKLY_SCHEDULE( + "주간 일정", + "매주 월,수,금에 반복되는 일정입니다.", + LocalDateTime.of(2024, 10, 20, 14, 0), + LocalDateTime.of(2024, 10, 20, 15, 0), + LocalDate.of(2024, 10, 20), + LocalDate.of(2025, 1, 1), + RepetitionCycle.WEEK, + ((1 << 1) | (1 << 3) | (1 << 5)), // 월, 수, 금 반복 + 1 // 매주 반복 + ), + WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK( + "2주 마다 반복되는 일정", + "2주 마다 반복되는 일정입니다.", + LocalDateTime.of(2024, 10, 20, 14, 0), + LocalDateTime.of(2024, 10, 21, 14, 0), + LocalDate.of(2024, 10, 20), + LocalDate.of(2025, 1, 1), + RepetitionCycle.WEEK, + null, // 월, 수, 금 반복 + 2 // 매주 반복 + ), + MONTHLY_SCHEDULE( + "월간 일정", + "매달 첫째 주 화요일에 반복되는 일정입니다.", + LocalDateTime.of(2024, 10, 20, 10, 0), + LocalDateTime.of(2024, 10, 21, 11, 0), + LocalDate.of(2024, 10, 20), + LocalDate.of(2025, 2, 1), + RepetitionCycle.MONTH, + 2, // 화요일 + 1 // 매월 반복 + ), + YEARLY_SCHEDULE( + "연간 일정", + "매년 10월 20일에 반복되는 일정입니다.", + LocalDateTime.of(2024, 10, 20, 10, 0), + LocalDateTime.of(2024, 10, 21, 11, 0), + LocalDate.of(2024, 10, 20), + LocalDate.of(2026, 10, 20), + RepetitionCycle.YEAR, + null, + null + ); + + private final String title; + private final String description; + private final LocalDateTime startDateTime; + private final LocalDateTime endDateTime; + private final LocalDate repetitionStartDate; + private final LocalDate repetitionEndDate; + private final RepetitionCycle repetitionCycle; + private final Integer dayOfWeek; + private final Integer weekInterval; + + ScheduleFixture(String title, String description, LocalDateTime startDateTime, LocalDateTime endDateTime, + LocalDate repetitionStartDate, LocalDate repetitionEndDate, RepetitionCycle repetitionCycle, + Integer dayOfWeek, Integer weekInterval) { + this.title = title; + this.description = description; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + this.repetitionStartDate = repetitionStartDate; + this.repetitionEndDate = repetitionEndDate; + this.repetitionCycle = repetitionCycle; + this.dayOfWeek = dayOfWeek; + this.weekInterval = weekInterval; + } + + public Schedule toEntity(ScheduleInfo scheduleInfo) { + return Schedule.builder() + .scheduleInfo(scheduleInfo) + .title(this.title) + .description(this.description) + .startDateTime(this.startDateTime) + .endDateTime(this.endDateTime) + .repetitionStartDate(this.repetitionStartDate) + .repetitionEndDate(this.repetitionEndDate) + .repetitionCycle(this.repetitionCycle) + .dayOfWeek(this.dayOfWeek) + .weekInterval(this.weekInterval) + .build(); + } + + public Schedule toObject() { + return Schedule.builder() + .title(this.title) + .description(this.description) + .startDateTime(this.startDateTime) + .endDateTime(this.endDateTime) + .repetitionStartDate(this.repetitionStartDate) + .repetitionEndDate(this.repetitionEndDate) + .repetitionCycle(this.repetitionCycle) + .dayOfWeek(this.dayOfWeek) + .weekInterval(this.weekInterval) + .build(); + } + +} \ No newline at end of file diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/ScheduleInfoFixture.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/ScheduleInfoFixture.java new file mode 100644 index 0000000..9ddf9ab --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/ScheduleInfoFixture.java @@ -0,0 +1,35 @@ +package com.wypl.wyplcore; + +import com.wypl.jpacalendardomain.calendar.domain.Calendar; +import com.wypl.jpacalendardomain.calendar.domain.ScheduleInfo; + +import lombok.Getter; + +@Getter +public enum ScheduleInfoFixture { + + PRIVATE_SCHEDULE_INFO(1L), + GROUP_SCHEDULE_INFO_1(2L), + GROUP_SCHEDULE_INFO_2(3L); + + private final Long creatorId; + + ScheduleInfoFixture(Long creatorId) { + this.creatorId = creatorId; + } + + public ScheduleInfo toObject(Calendar calendar) { + return ScheduleInfo.builder() + .calendar(calendar) + .creatorId(this.creatorId) + .build(); + } + + public ScheduleInfo toEntity(Calendar calendar) { + return ScheduleInfo.builder() + .calendar(calendar) + .creatorId(this.creatorId) + .build(); + } + +} \ No newline at end of file diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategyConfigTest.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategyConfigTest.java new file mode 100644 index 0000000..b33bae2 --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/calendar/service/strategy/CalendarStrategyConfigTest.java @@ -0,0 +1,46 @@ +package com.wypl.wyplcore.calendar.service.strategy; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; + +import com.wypl.wyplcore.schedule.data.CalendarType; + +@SpringBootTest +class CalendarStrategyConfigTest { + + @Autowired + private ApplicationContext applicationContext; + + @Test + void testRegisterBean() { + assertNotNull(applicationContext.getBean(CalendarStrategyConfig.class)); + assertTrue(applicationContext.containsBean("calendarStrategyMap")); + + Map instance = (Map)applicationContext.getBean("calendarStrategyMap"); + for (Map.Entry entry : instance.entrySet()) { + assertThat(entry.getKey()).isInstanceOf(CalendarType.class); + assertThat(entry.getValue()).isInstanceOf(CalendarStrategy.class); + System.out.println(entry.getKey() + " : " + entry.getValue()); + } + } + + @Test + void testStrategyFlyweight() { + + // Flyweight(CalendarStrategy)의 공유 역할을 하는 calendarStrategyMap이 싱글톤으로 관리되는 지 확인 + Map instance = (Map)applicationContext.getBean("calendarStrategyMap"); + Map instance2 = (Map)applicationContext.getBean("calendarStrategyMap"); + assertEquals(instance, instance2); + + // 여러 번 호출 시 같은 전략 인스턴스가 호출되는 지 확인 + assertEquals(instance.get("DAY"), instance2.get("DAY")); + } +} + diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/DayRepetitionStrategyTest.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/DayRepetitionStrategyTest.java new file mode 100644 index 0000000..122456c --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/DayRepetitionStrategyTest.java @@ -0,0 +1,89 @@ +package com.wypl.wyplcore.schedule.service.repetition; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.ScheduleFixture; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; + +@SpringBootTest +class DayRepetitionStrategyTest { + + private Schedule dayRepetitionSchedule; + + @BeforeEach + void setUpDayRepetitionSchedule() { + dayRepetitionSchedule = mock(Schedule.class); + when(dayRepetitionSchedule.getId()).thenReturn(1L); + when(dayRepetitionSchedule.getTitle()).thenReturn(ScheduleFixture.DAILY_SCHEDULE.getTitle()); + when(dayRepetitionSchedule.getDescription()).thenReturn(ScheduleFixture.DAILY_SCHEDULE.getDescription()); + when(dayRepetitionSchedule.getStartDateTime()).thenReturn(ScheduleFixture.DAILY_SCHEDULE.getStartDateTime()); + when(dayRepetitionSchedule.getEndDateTime()).thenReturn(ScheduleFixture.DAILY_SCHEDULE.getEndDateTime()); + when(dayRepetitionSchedule.getRepetitionStartDate()).thenReturn( + ScheduleFixture.DAILY_SCHEDULE.getRepetitionStartDate()); + when(dayRepetitionSchedule.getRepetitionEndDate()).thenReturn( + ScheduleFixture.DAILY_SCHEDULE.getRepetitionEndDate()); + when(dayRepetitionSchedule.getRepetitionCycle()).thenReturn( + ScheduleFixture.DAILY_SCHEDULE.getRepetitionCycle()); + when(dayRepetitionSchedule.getDayOfWeek()).thenReturn(ScheduleFixture.DAILY_SCHEDULE.getDayOfWeek()); + when(dayRepetitionSchedule.getWeekInterval()).thenReturn(ScheduleFixture.DAILY_SCHEDULE.getWeekInterval()); + when(dayRepetitionSchedule.isRepetition()).thenReturn(true); + } + + @Test + void getSchedulesResponsesForToday() { + + // given: dayRepetitionSchedule의 시작일로 오늘을 설정 + LocalDate startDate = dayRepetitionSchedule.getRepetitionStartDate(); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + dayRepetitionSchedule, startDate, startDate); + + // then + assertEquals(1, scheduleResponses.size()); + } + + @Test + void getSchedulesResponsesForWeek() { + + // given: dayRepetitionSchedule의 시작일 기준 다음 주 조회 (월요일 ~ 일요일) + LocalDate startDate = dayRepetitionSchedule.getRepetitionStartDate().with(TemporalAdjusters.previousOrSame( + DayOfWeek.MONDAY)).plusWeeks(1); + LocalDate endDate = startDate.plusDays(6); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + dayRepetitionSchedule, startDate, endDate); + + // then + assertEquals(7, scheduleResponses.size()); + + } + + @Test + void getSchedulesResponsesForMonth() { + + // given: dayRepetitionSchedule의 시작일 기준 월 조회 (2024년 10월) + LocalDate startDate = dayRepetitionSchedule.getRepetitionStartDate().with(TemporalAdjusters.firstDayOfMonth()); + LocalDate endDate = startDate.with(TemporalAdjusters.lastDayOfMonth()); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + dayRepetitionSchedule, startDate, endDate); + + // then + assertEquals(12, scheduleResponses.size()); + + } +} \ No newline at end of file diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/MonthRepetitionStrategyTest.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/MonthRepetitionStrategyTest.java new file mode 100644 index 0000000..8a96573 --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/MonthRepetitionStrategyTest.java @@ -0,0 +1,78 @@ +package com.wypl.wyplcore.schedule.service.repetition; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDate; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.ScheduleFixture; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; +import com.wypl.wyplcore.schedule.service.repetition.strategy.MonthRepetitionStrategy; + +@SpringBootTest +class MonthRepetitionStrategyTest { + + private Schedule monthRepetitionSchedule; + + @BeforeEach + void setUpMonthRepetitionSchedule() { + monthRepetitionSchedule = mock(Schedule.class); + when(monthRepetitionSchedule.getId()).thenReturn(1L); + when(monthRepetitionSchedule.getTitle()).thenReturn(ScheduleFixture.MONTHLY_SCHEDULE.getTitle()); + when(monthRepetitionSchedule.getDescription()).thenReturn(ScheduleFixture.MONTHLY_SCHEDULE.getDescription()); + when(monthRepetitionSchedule.getStartDateTime()).thenReturn( + ScheduleFixture.MONTHLY_SCHEDULE.getStartDateTime()); + when(monthRepetitionSchedule.getEndDateTime()).thenReturn(ScheduleFixture.MONTHLY_SCHEDULE.getEndDateTime()); + when(monthRepetitionSchedule.getRepetitionStartDate()).thenReturn( + ScheduleFixture.MONTHLY_SCHEDULE.getRepetitionStartDate()); + when(monthRepetitionSchedule.getRepetitionEndDate()).thenReturn( + ScheduleFixture.MONTHLY_SCHEDULE.getRepetitionEndDate()); + when(monthRepetitionSchedule.getRepetitionCycle()).thenReturn( + ScheduleFixture.MONTHLY_SCHEDULE.getRepetitionCycle()); + when(monthRepetitionSchedule.getDayOfWeek()).thenReturn(ScheduleFixture.MONTHLY_SCHEDULE.getDayOfWeek()); + when(monthRepetitionSchedule.getWeekInterval()).thenReturn(ScheduleFixture.MONTHLY_SCHEDULE.getWeekInterval()); + when(monthRepetitionSchedule.isRepetition()).thenReturn(true); + } + + @Test + @DisplayName("오늘 검색 조건으로 반복일정 조회") + void getSchedulesResponsesForToday() { + + // given: Set today as the start date of the monthRepetitionSchedule + LocalDate startDate = ScheduleFixture.MONTHLY_SCHEDULE.getRepetitionStartDate().plusMonths(1).plusDays(1); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + monthRepetitionSchedule, startDate, startDate); + + // then + assertEquals(1, scheduleResponses.size()); + + } + + @Test + @DisplayName("Month 검색 조건으로 반복일정 조회") + void getSchedulesResponsesForMonth() { + + // given: Set today as the start date of the monthRepetitionSchedule + LocalDate startDate = ScheduleFixture.MONTHLY_SCHEDULE.getRepetitionStartDate().plusMonths(1).plusDays(1); + LocalDate endDate = startDate.plusYears(1); + + System.out.println("startDate -> " + startDate + ", endDate -> " + endDate); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + monthRepetitionSchedule, startDate, endDate); + + // then + assertEquals(3, scheduleResponses.size()); + + } +} \ No newline at end of file diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/WeekRepetitionStrategyTest.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/WeekRepetitionStrategyTest.java new file mode 100644 index 0000000..5fd3569 --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/WeekRepetitionStrategyTest.java @@ -0,0 +1,215 @@ +package com.wypl.wyplcore.schedule.service.repetition; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.ScheduleFixture; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; + +@SpringBootTest +class WeekRepetitionStrategyTest { + + private Schedule weekRepetitionScheduleWithDayOfWeek; + private Schedule weekRepetitionScheduleWithoutDayOfWeek; + + @BeforeEach + void setUpWeekRepetitionSchedule() { + weekRepetitionScheduleWithDayOfWeek = mock(Schedule.class); + when(weekRepetitionScheduleWithDayOfWeek.getId()).thenReturn(1L); + when(weekRepetitionScheduleWithDayOfWeek.getTitle()).thenReturn(ScheduleFixture.WEEKLY_SCHEDULE.getTitle()); + when(weekRepetitionScheduleWithDayOfWeek.getDescription()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE.getDescription()); + when(weekRepetitionScheduleWithDayOfWeek.getStartDateTime()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE.getStartDateTime()); + when(weekRepetitionScheduleWithDayOfWeek.getEndDateTime()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE.getEndDateTime()); + when(weekRepetitionScheduleWithDayOfWeek.getRepetitionStartDate()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE.getRepetitionStartDate()); + when(weekRepetitionScheduleWithDayOfWeek.getRepetitionEndDate()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE.getRepetitionEndDate()); + when(weekRepetitionScheduleWithDayOfWeek.getRepetitionCycle()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE.getRepetitionCycle()); + when(weekRepetitionScheduleWithDayOfWeek.getDayOfWeek()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE.getDayOfWeek()); + when(weekRepetitionScheduleWithDayOfWeek.getWeekInterval()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE.getWeekInterval()); + when(weekRepetitionScheduleWithDayOfWeek.isRepetition()).thenReturn(true); + when(weekRepetitionScheduleWithDayOfWeek.existsDayOfWeek()).thenReturn(true); + } + + @BeforeEach + void setUpWeekRepetitionScheduleWithoutDayOfWeek() { + weekRepetitionScheduleWithoutDayOfWeek = mock(Schedule.class); + when(weekRepetitionScheduleWithoutDayOfWeek.getId()).thenReturn(1L); + when(weekRepetitionScheduleWithoutDayOfWeek.getTitle()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getTitle()); + when(weekRepetitionScheduleWithoutDayOfWeek.getDescription()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getDescription()); + when(weekRepetitionScheduleWithoutDayOfWeek.getStartDateTime()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getStartDateTime()); + when(weekRepetitionScheduleWithoutDayOfWeek.getEndDateTime()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getEndDateTime()); + when(weekRepetitionScheduleWithoutDayOfWeek.getRepetitionStartDate()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getRepetitionStartDate()); + when(weekRepetitionScheduleWithoutDayOfWeek.getRepetitionEndDate()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getRepetitionEndDate()); + when(weekRepetitionScheduleWithoutDayOfWeek.getRepetitionCycle()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getRepetitionCycle()); + when(weekRepetitionScheduleWithoutDayOfWeek.getDayOfWeek()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getDayOfWeek()); + when(weekRepetitionScheduleWithoutDayOfWeek.getWeekInterval()).thenReturn( + ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getWeekInterval()); + when(weekRepetitionScheduleWithoutDayOfWeek.isRepetition()).thenReturn(true); + when(weekRepetitionScheduleWithoutDayOfWeek.existsDayOfWeek()).thenReturn(false); + } + + @Test + void getSchedulesResponsesForTodayWithDayOfWeek() { + + // given: weekRepetitionSchedule의 시작일로 오늘을 설정 + LocalDate startDate = ScheduleFixture.WEEKLY_SCHEDULE.getRepetitionStartDate().with( + TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + weekRepetitionScheduleWithDayOfWeek, startDate, startDate); + + // then + assertEquals(1, scheduleResponses.size()); + } + + @Test + void getSchedulesResponsesForWeekWithDayOfWeek() { + + // given: weekRepetitionSchedule의 시작일 기준 다음 주 조회 (월요일 ~ 일요일) + LocalDate startDate = ScheduleFixture.WEEKLY_SCHEDULE.getRepetitionStartDate() + .with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)); + LocalDate endDate = startDate.plusDays(6); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + weekRepetitionScheduleWithDayOfWeek, startDate, endDate); + + // then + assertEquals(3, scheduleResponses.size()); + } + + @Test + void getSchedulesResponsesForMonthWithDayOfWeek() { + + // given: weekRepetitionSchedule의 시작일 기준 월 조회 (2024년 10월) + LocalDate startDate = ScheduleFixture.WEEKLY_SCHEDULE.getRepetitionStartDate() + .with(TemporalAdjusters.firstDayOfMonth()); + LocalDate endDate = startDate.with(TemporalAdjusters.lastDayOfMonth()); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + weekRepetitionScheduleWithDayOfWeek, startDate, endDate); + + // then + for (ScheduleFindResponse scheduleResponse : scheduleResponses) { + System.out.println(scheduleResponse.title() + " : " + scheduleResponse.startDateTime() + " - " + + scheduleResponse.endDateTime()); + } + assertEquals(5, scheduleResponses.size()); + + } + + @Test + @DisplayName("요일이 설정되지 않은 일정의 DAY 검색 조건으로 반복일정 조회") + void getSchedulesResponsesForTodayWithoutDayOfWeek() { + + // given: Set today after 1 week, and the range is 'RepetitionStartDate 요일 < today < RepetitionEndDate 요일' + LocalDate today = ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getRepetitionStartDate() + .plusDays(1) + .plusWeeks(1); + System.out.println("today -> " + today); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + weekRepetitionScheduleWithoutDayOfWeek, today, today); + + // then + for (ScheduleFindResponse scheduleResponse : scheduleResponses) { + System.out.println(scheduleResponse.title() + " : " + scheduleResponse.startDateTime() + " - " + + scheduleResponse.endDateTime()); + } + assertEquals(1, scheduleResponses.size()); + } + + @Test + @DisplayName("요일이 설정되지 않은 일정의 WEEK 검색 조건으로 반복일정 조회") + void getSchedulesResponsesForWeekWithoutDayOfWeek() { + + // given: Set today after 1 week, and the range is 'RepetitionStartDate 요일 < today < RepetitionEndDate 요일' + LocalDate startDate = ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getRepetitionStartDate() + .with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)); + LocalDate endDate = startDate.plusDays(6); + System.out.println("startDate -> " + startDate + ", endDate -> " + endDate); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + weekRepetitionScheduleWithoutDayOfWeek, startDate, endDate); + + // then + for (ScheduleFindResponse scheduleResponse : scheduleResponses) { + System.out.println(scheduleResponse.title() + " : " + scheduleResponse.startDateTime() + " - " + + scheduleResponse.endDateTime()); + } + assertEquals(1, scheduleResponses.size()); + } + + @Test + @DisplayName("요일이 설정되지 않은 일정의 WEEK 검색 조건으로 반복일정 조회 - 반복 주에 포함되지 않는 조회 조건") + void getSchedulesResponsesForWeekWithoutDayOfWeek2() { + + // given: Set today after 1 week, and the range is 'RepetitionStartDate 요일 < today < RepetitionEndDate 요일' + LocalDate startDate = ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getRepetitionStartDate().plusWeeks(1); + LocalDate endDate = startDate.plusDays(6); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + weekRepetitionScheduleWithoutDayOfWeek, startDate, endDate); + + // then + for (ScheduleFindResponse scheduleResponse : scheduleResponses) { + System.out.println(scheduleResponse.title() + " : " + scheduleResponse.startDateTime() + " - " + + scheduleResponse.endDateTime()); + } + assertEquals(0, scheduleResponses.size()); + } + + @Test + @DisplayName("요일이 설정되지 않은 일정의 MONTH 검색 조건으로 반복일정 조회") + void getSchedulesResponsesForMonthWithoutDayOfWeek() { + + // given: Set today after 1 week, and the range is 'RepetitionStartDate 요일 < today < RepetitionEndDate 요일' + LocalDate startDate = ScheduleFixture.WEEKLY_SCHEDULE_WITHOUT_DAY_OF_WEEK.getRepetitionStartDate() + .plusMonths(1) + .with(TemporalAdjusters.firstDayOfMonth()); + LocalDate endDate = startDate.with(TemporalAdjusters.lastDayOfMonth()); + System.out.println("startDate -> " + startDate + ", endDate -> " + endDate); + + // when + List scheduleResponses = RepetitionService.getScheduleResponses( + weekRepetitionScheduleWithoutDayOfWeek, startDate, endDate); + + // then + for (ScheduleFindResponse scheduleResponse : scheduleResponses) { + System.out.println(scheduleResponse.title() + " : " + scheduleResponse.startDateTime() + " - " + + scheduleResponse.endDateTime()); + } + assertEquals(2, scheduleResponses.size()); + } +} \ No newline at end of file diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/YearRepetitionStrategyTest.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/YearRepetitionStrategyTest.java new file mode 100644 index 0000000..a215764 --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/schedule/service/repetition/YearRepetitionStrategyTest.java @@ -0,0 +1,94 @@ +package com.wypl.wyplcore.schedule.service.repetition; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; +import com.wypl.wyplcore.ScheduleFixture; +import com.wypl.wyplcore.schedule.data.response.ScheduleFindResponse; +import com.wypl.wyplcore.schedule.service.repetition.strategy.YearRepetitionStrategy; + +@SpringBootTest +class YearRepetitionStrategyTest { + + private final YearRepetitionStrategy yearRepetitionStrategy = new YearRepetitionStrategy(); + private Schedule yearRepetitionSchedule; + + @BeforeEach + void setUpYearRepetitionSchedule() { + yearRepetitionSchedule = mock(Schedule.class); + when(yearRepetitionSchedule.getId()).thenReturn(1L); + when(yearRepetitionSchedule.getTitle()).thenReturn(ScheduleFixture.YEARLY_SCHEDULE.getTitle()); + when(yearRepetitionSchedule.getDescription()).thenReturn(ScheduleFixture.YEARLY_SCHEDULE.getDescription()); + when(yearRepetitionSchedule.getStartDateTime()).thenReturn(ScheduleFixture.YEARLY_SCHEDULE.getStartDateTime()); + when(yearRepetitionSchedule.getEndDateTime()).thenReturn(ScheduleFixture.YEARLY_SCHEDULE.getEndDateTime()); + when(yearRepetitionSchedule.getRepetitionStartDate()).thenReturn( + ScheduleFixture.YEARLY_SCHEDULE.getRepetitionStartDate()); + when(yearRepetitionSchedule.getRepetitionEndDate()).thenReturn( + ScheduleFixture.YEARLY_SCHEDULE.getRepetitionEndDate()); + when(yearRepetitionSchedule.getRepetitionCycle()).thenReturn( + ScheduleFixture.YEARLY_SCHEDULE.getRepetitionCycle()); + when(yearRepetitionSchedule.getDayOfWeek()).thenReturn(ScheduleFixture.YEARLY_SCHEDULE.getDayOfWeek()); + when(yearRepetitionSchedule.getWeekInterval()).thenReturn(ScheduleFixture.YEARLY_SCHEDULE.getWeekInterval()); + when(yearRepetitionSchedule.isRepetition()).thenReturn(true); + } + + @Test + @DisplayName("오늘 검색 조건으로 반복일정 조회 - 결과가 없는 경우") + void getSchedulesResponsesForNoResult() { + + // given + LocalDate startDate = ScheduleFixture.YEARLY_SCHEDULE.getRepetitionStartDate() + .with(TemporalAdjusters.firstDayOfYear()); + + // when + List scheduleResponses = yearRepetitionStrategy.getScheduleResponses( + yearRepetitionSchedule, startDate, startDate); + + // then + assertEquals(0, scheduleResponses.size()); + + } + + @Test + @DisplayName("오늘 검색 조건으로 반복일정 조회 - 결과가 1건인 경우") + void getSchedulesResponsesForToday() { + + // given + LocalDate startDate = ScheduleFixture.YEARLY_SCHEDULE.getRepetitionStartDate().plusYears(1).plusDays(1); + + // when + List scheduleResponses = yearRepetitionStrategy.getScheduleResponses( + yearRepetitionSchedule, startDate, startDate); + + // then + assertEquals(1, scheduleResponses.size()); + + } + + @Test + @DisplayName("Year 검색 조건으로 반복일정 조회") + void getSchedulesResponsesForYear() { + + // given + LocalDate startDate = ScheduleFixture.YEARLY_SCHEDULE.getRepetitionStartDate().plusDays(1); + LocalDate endDate = startDate.plusYears(2); + + // when + List scheduleResponses = yearRepetitionStrategy.getScheduleResponses( + yearRepetitionSchedule, startDate, endDate); + + // then + assertEquals(3, scheduleResponses.size()); + + } +} \ No newline at end of file diff --git a/application/wypl-image/src/main/java/com/wypl/wyplimage/exception/ImageErrorCode.java b/application/wypl-image/src/main/java/com/wypl/wyplimage/exception/ImageErrorCode.java index c040dfa..9b72218 100644 --- a/application/wypl-image/src/main/java/com/wypl/wyplimage/exception/ImageErrorCode.java +++ b/application/wypl-image/src/main/java/com/wypl/wyplimage/exception/ImageErrorCode.java @@ -12,8 +12,7 @@ public enum ImageErrorCode implements ServerErrorCode { INVALID_IMAGE_PATH(500, "IMAGE_004", "이미지 처리중 오류가 발생했습니다."), INVALID_FILE_PATH(500, "IMAGE_005", "이미지 처리중 오류가 발생했습니다."), NOT_EXISTED_COMMAND(500, "IMAGE_006", "이미지 처리중 오류가 발생했습니다."), - PARENT_PROCESS_DEAD(500, "IMAGE_007", "정상적으로 요청을 처리하지 못하였습니다.") - ; + PARENT_PROCESS_DEAD(500, "IMAGE_007", "정상적으로 요청을 처리하지 못하였습니다."); private final int statusCode; private final String errorCode; private final String message; diff --git a/build.gradle b/build.gradle index 6aff9bb..dc34602 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,7 @@ subprojects { compileOnly 'org.projectlombok:lombok' testImplementation 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' } tasks.named('bootJar') { diff --git a/domain/jpa-calendar-domain/build.gradle b/domain/jpa-calendar-domain/build.gradle index e883a9a..d643796 100644 --- a/domain/jpa-calendar-domain/build.gradle +++ b/domain/jpa-calendar-domain/build.gradle @@ -8,4 +8,28 @@ dependencies { implementation project(':common') implementation project(':domain:jpa-common') implementation project(':domain:jpa-member-domain') -} \ No newline at end of file + + + /* QueryDSL */ + implementation('com.querydsl:querydsl-jpa:5.0.0:jakarta') + annotationProcessor('com.querydsl:querydsl-apt:5.0.0:jakarta') + annotationProcessor('jakarta.annotation:jakarta.annotation-api') + annotationProcessor('jakarta.persistence:jakarta.persistence-api') +} + + +/ QueryDSL Start / +def generated = "src/main/generated" + +tasks.withType(JavaCompile).configureEach { + options.getGeneratedSourceOutputDirectory().set(file(generated)) +} + +sourceSets { + main.java.srcDirs += [generated] +} + +clean { + delete file(generated) +} +/ QueryDSL End */ \ No newline at end of file diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/ConvertibleSchedule.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/ConvertibleSchedule.java index 1ac9c06..2752188 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/ConvertibleSchedule.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/ConvertibleSchedule.java @@ -5,21 +5,21 @@ public interface ConvertibleSchedule { - String getTitle(); + String getTitle(); - String getDescription(); + String getDescription(); - LocalDateTime getStartDateTime(); + LocalDateTime getStartDateTime(); - LocalDateTime getEndDateTime(); + LocalDateTime getEndDateTime(); - LocalDate getRepetitionStartDate(); + LocalDate getRepetitionStartDate(); - LocalDate getRepetitionEndDate(); + LocalDate getRepetitionEndDate(); - RepetitionCycle getRepetitionCycle(); + RepetitionCycle getRepetitionCycle(); - Integer getDayOfWeek(); + Integer getDayOfWeek(); - Integer getWeekInterval(); + Integer getWeekInterval(); } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/ConvertibleScheduleInfo.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/ConvertibleScheduleInfo.java index b5779e5..2efb43d 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/ConvertibleScheduleInfo.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/ConvertibleScheduleInfo.java @@ -2,6 +2,6 @@ public interface ConvertibleScheduleInfo { - Long getCreatorId(); + Long getCreatorId(); } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/RepetitionCycle.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/RepetitionCycle.java index 8f74c36..9e009ad 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/RepetitionCycle.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/data/RepetitionCycle.java @@ -1,8 +1,8 @@ package com.wypl.jpacalendardomain.calendar.data; public enum RepetitionCycle { - DAY, - WEEK, - MONTH, - YEAR + DAY, + WEEK, + MONTH, + YEAR } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/Calendar.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/Calendar.java index 11f3edb..b29e134 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/Calendar.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/Calendar.java @@ -1,16 +1,23 @@ package com.wypl.jpacalendardomain.calendar.domain; -import com.wypl.jpacommon.JpaBaseEntity; -import jakarta.persistence.*; +import java.util.List; + import org.hibernate.annotations.SQLRestriction; +import com.wypl.jpacommon.JpaBaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import java.util.List; - @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @SQLRestriction("deleted_at is null") diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/MemberCalendar.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/MemberCalendar.java index f9dea0d..f9e2ade 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/MemberCalendar.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/MemberCalendar.java @@ -1,10 +1,10 @@ package com.wypl.jpacalendardomain.calendar.domain; -import com.wypl.jpacommon.JpaBaseEntity; import org.hibernate.annotations.SQLRestriction; import com.wypl.common.Color; import com.wypl.jpacalendardomain.calendar.data.InviteStatus; +import com.wypl.jpacommon.JpaBaseEntity; import com.wypl.jpamemberdomain.member.domain.Member; import jakarta.persistence.Column; diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/Schedule.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/Schedule.java index a45c46d..8f8cdb9 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/Schedule.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/Schedule.java @@ -1,13 +1,28 @@ package com.wypl.jpacalendardomain.calendar.domain; +import java.time.LocalDate; +import java.time.LocalDateTime; + +import org.hibernate.annotations.SQLRestriction; + import com.wypl.jpacalendardomain.calendar.data.RepetitionCycle; import com.wypl.jpacommon.JpaBaseEntity; -import jakarta.persistence.*; -import lombok.*; -import org.hibernate.annotations.SQLRestriction; -import java.time.LocalDate; -import java.time.LocalDateTime; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -16,55 +31,67 @@ @Table(name = "schedule_tbl") public class Schedule extends JpaBaseEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "schedule_id") - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "schedule_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "schedule_info_id", nullable = false) + private ScheduleInfo scheduleInfo; + + @Column(name = "title", length = 100) + private String title; + + @Column(name = "description") + private String description; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "schedule_info_id", nullable = false) - private ScheduleInfo scheduleInfo; + @Column(name = "start_date_time", nullable = false) + private LocalDateTime startDateTime; - @Column(name = "title", length = 100) - private String title; + @Column(name = "end_date_time", nullable = false) + private LocalDateTime endDateTime; - @Column(name = "description") - private String description; + @Column(name = "repetition_start_date") + private LocalDate repetitionStartDate; - @Column(name = "start_datetime", nullable = false) - private LocalDateTime startDateTime; + @Column(name = "repetition_end_date") + private LocalDate repetitionEndDate; - @Column(name = "end_datetime", nullable = false) - private LocalDateTime endDateTime; + @Getter + @Enumerated(EnumType.STRING) + private RepetitionCycle repetitionCycle; // 반복 주기 (일, 주, 달, 년) - @Column(name = "repetition_start_date") - private LocalDate repetitionStartDate; + @Column(name = "day_of_week") + private Integer dayOfWeek; // 반복 요일: Bit Masking - @Column(name = "repetition_end_date") - private LocalDate repetitionEndDate; + @Column(name = "week_interval") + private Integer weekInterval; // 주 반복 (1~3) - @Enumerated(EnumType.STRING) - private RepetitionCycle repetitionCycle; // 반복 주기 (일, 주, 달, 년) + // Todo: Review Mapping - @Column(name = "day_of_week") - private Integer dayOfWeek; // 반복 요일 + @Builder + public Schedule(ScheduleInfo scheduleInfo, String title, String description, LocalDateTime startDateTime, + LocalDateTime endDateTime, LocalDate repetitionStartDate, LocalDate repetitionEndDate, + RepetitionCycle repetitionCycle, Integer dayOfWeek, Integer weekInterval) { + this.scheduleInfo = scheduleInfo; + this.title = title; + this.description = description; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + this.repetitionStartDate = repetitionStartDate; + this.repetitionEndDate = repetitionEndDate; + this.repetitionCycle = repetitionCycle; + this.dayOfWeek = dayOfWeek; + this.weekInterval = weekInterval; + } - @Column(name = "week_interval") - private Integer weekInterval; // 주 반복 + public boolean isRepetition() { + return repetitionCycle != null; + } - // Todo: Review Mapping + public boolean existsDayOfWeek() { + return dayOfWeek != null; + } - @Builder - public Schedule(ScheduleInfo scheduleInfo, String title, String description, LocalDateTime startDateTime, LocalDateTime endDateTime, LocalDate repetitionStartDate, LocalDate repetitionEndDate, RepetitionCycle repetitionCycle, Integer dayOfWeek, Integer weekInterval) { - this.scheduleInfo = scheduleInfo; - this.title = title; - this.description = description; - this.startDateTime = startDateTime; - this.endDateTime = endDateTime; - this.repetitionStartDate = repetitionStartDate; - this.repetitionEndDate = repetitionEndDate; - this.repetitionCycle = repetitionCycle; - this.dayOfWeek = dayOfWeek; - this.weekInterval = weekInterval; - } } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/ScheduleInfo.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/ScheduleInfo.java index 958331f..99ef5c0 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/ScheduleInfo.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/ScheduleInfo.java @@ -1,14 +1,25 @@ package com.wypl.jpacalendardomain.calendar.domain; +import java.util.List; + +import org.hibernate.annotations.SQLRestriction; + import com.wypl.jpacommon.JpaBaseEntity; -import jakarta.persistence.*; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.SQLRestriction; - -import java.util.List; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -17,24 +28,24 @@ @Table(name = "schedule_info_tbl") public class ScheduleInfo extends JpaBaseEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "schedule_info_id") - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "schedule_info_id") + private Long id; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "calendar_id", nullable = false) - private Calendar calendar; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "calendar_id", nullable = false) + private Calendar calendar; - @Column(name = "creator_id") - private Long creatorId; + @Column(name = "creator_id") + private Long creatorId; - @OneToMany(mappedBy = "scheduleInfo") - private List schedules; + @OneToMany(mappedBy = "scheduleInfo") + private List schedules; - @Builder - public ScheduleInfo(Calendar calendar, Long creatorId) { - this.calendar = calendar; - this.creatorId = creatorId; - } + @Builder + public ScheduleInfo(Calendar calendar, Long creatorId) { + this.calendar = calendar; + this.creatorId = creatorId; + } } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleInfoMapper.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleInfoMapper.java index c1435bb..f5fb05f 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleInfoMapper.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleInfoMapper.java @@ -6,17 +6,17 @@ public class ScheduleInfoMapper { - public static ScheduleInfo toJpaScheduleInfo(Calendar calendar, ConvertibleScheduleInfo scheduleInfo) { - return ScheduleInfo.builder() - .creatorId(scheduleInfo.getCreatorId()) - .calendar(calendar) - .build(); - } + public static ScheduleInfo toJpaScheduleInfo(Calendar calendar, ConvertibleScheduleInfo scheduleInfo) { + return ScheduleInfo.builder() + .creatorId(scheduleInfo.getCreatorId()) + .calendar(calendar) + .build(); + } - public static ScheduleInfo toJpaScheduleInfo(Calendar calendar, long id) { - return ScheduleInfo.builder() - .creatorId(id) - .calendar(calendar) - .build(); - } + public static ScheduleInfo toJpaScheduleInfo(Calendar calendar, long id) { + return ScheduleInfo.builder() + .creatorId(id) + .calendar(calendar) + .build(); + } } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleMapper.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleMapper.java index fc05a9d..c738179 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleMapper.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleMapper.java @@ -6,18 +6,18 @@ public class ScheduleMapper { - public static Schedule toJpaSchedule(ConvertibleSchedule convertibleSchedule, ScheduleInfo scheduleInfo) { - return Schedule.builder() - .scheduleInfo(scheduleInfo) - .title(convertibleSchedule.getTitle()) - .description(convertibleSchedule.getDescription()) - .startDateTime(convertibleSchedule.getStartDateTime()) - .endDateTime(convertibleSchedule.getEndDateTime()) - .repetitionStartDate(convertibleSchedule.getRepetitionStartDate()) - .repetitionEndDate(convertibleSchedule.getRepetitionEndDate()) - .repetitionCycle(convertibleSchedule.getRepetitionCycle()) - .dayOfWeek(convertibleSchedule.getDayOfWeek()) - .weekInterval(convertibleSchedule.getWeekInterval()) - .build(); - } + public static Schedule toJpaSchedule(ConvertibleSchedule convertibleSchedule, ScheduleInfo scheduleInfo) { + return Schedule.builder() + .scheduleInfo(scheduleInfo) + .title(convertibleSchedule.getTitle()) + .description(convertibleSchedule.getDescription()) + .startDateTime(convertibleSchedule.getStartDateTime()) + .endDateTime(convertibleSchedule.getEndDateTime()) + .repetitionStartDate(convertibleSchedule.getRepetitionStartDate()) + .repetitionEndDate(convertibleSchedule.getRepetitionEndDate()) + .repetitionCycle(convertibleSchedule.getRepetitionCycle()) + .dayOfWeek(convertibleSchedule.getDayOfWeek()) + .weekInterval(convertibleSchedule.getWeekInterval()) + .build(); + } } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/CalendarRepository.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/CalendarRepository.java new file mode 100644 index 0000000..408948a --- /dev/null +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/CalendarRepository.java @@ -0,0 +1,8 @@ +package com.wypl.jpacalendardomain.calendar.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.wypl.jpacalendardomain.calendar.domain.Calendar; + +public interface CalendarRepository extends JpaRepository { +} diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleInfoRepository.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleInfoRepository.java index 4564f7a..1221afe 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleInfoRepository.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleInfoRepository.java @@ -1,7 +1,8 @@ package com.wypl.jpacalendardomain.calendar.repository; -import com.wypl.jpacalendardomain.calendar.domain.ScheduleInfo; import org.springframework.data.jpa.repository.JpaRepository; +import com.wypl.jpacalendardomain.calendar.domain.ScheduleInfo; + public interface ScheduleInfoRepository extends JpaRepository { } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepository.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepository.java index a6cd842..d8e17bb 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepository.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepository.java @@ -1,7 +1,8 @@ package com.wypl.jpacalendardomain.calendar.repository; -import com.wypl.jpacalendardomain.calendar.domain.Schedule; import org.springframework.data.jpa.repository.JpaRepository; -public interface ScheduleRepository extends JpaRepository { +import com.wypl.jpacalendardomain.calendar.domain.Schedule; + +public interface ScheduleRepository extends JpaRepository, ScheduleRepositoryCustom { } diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepositoryCustom.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepositoryCustom.java new file mode 100644 index 0000000..455188b --- /dev/null +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepositoryCustom.java @@ -0,0 +1,11 @@ +package com.wypl.jpacalendardomain.calendar.repository; + +import java.time.LocalDate; +import java.util.List; + +import com.wypl.jpacalendardomain.calendar.domain.Schedule; + +public interface ScheduleRepositoryCustom { + List findByCalendarIdAndBetweenStartDateAndEndDate(long calendarId, LocalDate startDate, + LocalDate endDate); +} diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepositoryCustomImpl.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepositoryCustomImpl.java new file mode 100644 index 0000000..195e83b --- /dev/null +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/repository/ScheduleRepositoryCustomImpl.java @@ -0,0 +1,27 @@ +package com.wypl.jpacalendardomain.calendar.repository; + +import java.time.LocalDate; +import java.util.List; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.wypl.jpacalendardomain.calendar.domain.QSchedule; +import com.wypl.jpacalendardomain.calendar.domain.Schedule; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ScheduleRepositoryCustomImpl implements ScheduleRepositoryCustom { + + private final JPAQueryFactory jpaQueryFactory; + private final QSchedule schedule = QSchedule.schedule; + + @Override + public List findByCalendarIdAndBetweenStartDateAndEndDate(long calendarId, LocalDate startDate, + LocalDate endDate) { + return jpaQueryFactory.selectFrom(schedule) + .where(schedule.scheduleInfo.calendar.id.eq(calendarId) + .and(schedule.repetitionStartDate.loe(endDate) + .and(schedule.repetitionEndDate.goe(startDate)))) + .fetch(); + } +} diff --git a/domain/jpa-common/build.gradle b/domain/jpa-common/build.gradle index 183958e..49bc941 100644 --- a/domain/jpa-common/build.gradle +++ b/domain/jpa-common/build.gradle @@ -10,4 +10,31 @@ dependencies { api('org.springframework.boot:spring-boot-starter-data-jpa') runtimeOnly('com.h2database:h2') + + /* MapStruct */ + api('org.mapstruct:mapstruct:1.5.5.Final') + annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' + + /* QueryDSL */ + implementation('com.querydsl:querydsl-jpa:5.0.0:jakarta') + annotationProcessor('com.querydsl:querydsl-apt:5.0.0:jakarta') + annotationProcessor('jakarta.annotation:jakarta.annotation-api') + annotationProcessor('jakarta.persistence:jakarta.persistence-api') +} + + +/ QueryDSL Start / +def generated = "src/main/generated" + +tasks.withType(JavaCompile).configureEach { + options.getGeneratedSourceOutputDirectory().set(file(generated)) +} + +sourceSets { + main.java.srcDirs += [generated] +} + +clean { + delete file(generated) } +/ QueryDSL End */ \ No newline at end of file diff --git a/domain/jpa-common/src/main/java/com/wypl/jpacommon/JpaQueryFactoryConfig.java b/domain/jpa-common/src/main/java/com/wypl/jpacommon/JpaQueryFactoryConfig.java new file mode 100644 index 0000000..15074bf --- /dev/null +++ b/domain/jpa-common/src/main/java/com/wypl/jpacommon/JpaQueryFactoryConfig.java @@ -0,0 +1,14 @@ +package com.wypl.jpacommon; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class JpaQueryFactoryConfig { + @Bean + public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) { + return new JPAQueryFactory(entityManager); + } +} diff --git a/domain/jpa-member-domain/build.gradle b/domain/jpa-member-domain/build.gradle index 0682ea4..a6c58f7 100644 --- a/domain/jpa-member-domain/build.gradle +++ b/domain/jpa-member-domain/build.gradle @@ -8,4 +8,26 @@ dependencies { implementation project(':domain:jpa-common') implementation project(':common') -} \ No newline at end of file + /* QueryDSL */ + implementation('com.querydsl:querydsl-jpa:5.0.0:jakarta') + annotationProcessor('com.querydsl:querydsl-apt:5.0.0:jakarta') + annotationProcessor('jakarta.annotation:jakarta.annotation-api') + annotationProcessor('jakarta.persistence:jakarta.persistence-api') +} + + +/ QueryDSL Start / +def generated = "src/main/generated" + +tasks.withType(JavaCompile).configureEach { + options.getGeneratedSourceOutputDirectory().set(file(generated)) +} + +sourceSets { + main.java.srcDirs += [generated] +} + +clean { + delete file(generated) +} +/ QueryDSL End */ \ No newline at end of file diff --git a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberErrorCode.java b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberErrorCode.java index b8da3e3..e5dc7fc 100644 --- a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberErrorCode.java +++ b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberErrorCode.java @@ -6,8 +6,7 @@ @Getter public enum MemberErrorCode implements ServerErrorCode { - NO_SUCH_MEMBER(400, "MEMBER_001", "존재하지 않는 회원입니다.") - ; + NO_SUCH_MEMBER(400, "MEMBER_001", "존재하지 않는 회원입니다."); private final int statusCode; private final String errorCode; diff --git a/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/reviewcontents/domain/ReviewContent.java b/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/reviewcontents/domain/ReviewContent.java index c38e222..14cedf2 100644 --- a/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/reviewcontents/domain/ReviewContent.java +++ b/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/reviewcontents/domain/ReviewContent.java @@ -5,6 +5,7 @@ @Getter public abstract class ReviewContent { private final BlockType blockType; + protected ReviewContent(BlockType blockType) { this.blockType = blockType; }