-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 예약 조기종료 기능 구현 #969
base: dev
Are you sure you want to change the base?
feat: 예약 조기종료 기능 구현 #969
Changes from 12 commits
34a3798
2265a54
5e10d61
d0dea33
ed0580a
e19af2e
565b680
cb43e8e
c75ad9e
25cbf2f
eaa505b
d75d31b
76d3464
0f5e9bb
d910d49
bf0b974
aaeb59e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -98,4 +98,3 @@ public String toString() { | |
return startTime + " ~ " + endTimeAsString; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.woowacourse.zzimkkong.dto.reservation; | ||
|
||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
import javax.validation.constraints.Pattern; | ||
import java.time.ZonedDateTime; | ||
|
||
import static com.woowacourse.zzimkkong.dto.ValidatorMessage.RESERVATION_PW_FORMAT; | ||
import static com.woowacourse.zzimkkong.dto.ValidatorMessage.RESERVATION_PW_MESSAGE; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class ReservationUpdateWithPasswordWithoutEndTimeRequest extends ReservationCreateUpdateRequest { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 이해하기로는 만약 startTime, endTime 이 모두 필요 없다면 굳이 Request DTO 를 상속할 필요없이 더 간단한 DTO를 만들 수 있을 것 같습니다~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Request DTO를 상속해서 사용하는 것이 컨벤션이라고 생각해서 위와 같이 구현했습니다..! 상속없이 필요한 값만 받아서 간단한 DTO를 받는 방식으로 처리하겠습니다. |
||
@Pattern(regexp = RESERVATION_PW_FORMAT, message = RESERVATION_PW_MESSAGE) | ||
private String password; | ||
|
||
public ReservationUpdateWithPasswordWithoutEndTimeRequest( | ||
final ZonedDateTime startDateTime, | ||
final String password, | ||
final String name, | ||
final String description) { | ||
super(startDateTime, ZonedDateTime.now(), name, description); | ||
this.password = password; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,13 @@ | ||||||
package com.woowacourse.zzimkkong.exception.reservation; | ||||||
|
||||||
import com.woowacourse.zzimkkong.exception.ZzimkkongException; | ||||||
import org.springframework.http.HttpStatus; | ||||||
|
||||||
public class InvalidMinimumDurationTimeInEarlyStopException extends ZzimkkongException { | ||||||
|
||||||
private static final String MESSAGE = "조기 종료는 최소 5분 이후부터 가능합니다."; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
분이 가변적이어야 할 것 같아요~ Exception 생성자에 TimeUnit 관련 인자를 받아야겠네요. 참고할 만한 Exception 많으니 한 번 둘러보셔서 참고하시면 될 것 같습니다! |
||||||
|
||||||
public InvalidMinimumDurationTimeInEarlyStopException() { | ||||||
super(MESSAGE, HttpStatus.BAD_REQUEST); | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.woowacourse.zzimkkong.exception.reservation; | ||
|
||
import com.woowacourse.zzimkkong.exception.ZzimkkongException; | ||
import org.springframework.http.HttpStatus; | ||
|
||
public class NotCurrentReservationException extends ZzimkkongException { | ||
private static final String MESSAGE = "사용중인 예약에 대해서만 조기종료가 가능합니다."; | ||
|
||
public NotCurrentReservationException() { | ||
super(MESSAGE, HttpStatus.BAD_REQUEST); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,39 @@ | ||
package com.woowacourse.zzimkkong.service; | ||
|
||
import com.woowacourse.zzimkkong.domain.*; | ||
import com.woowacourse.zzimkkong.domain.Map; | ||
import com.woowacourse.zzimkkong.domain.Member; | ||
import com.woowacourse.zzimkkong.domain.Reservation; | ||
import com.woowacourse.zzimkkong.domain.ReservationTime; | ||
import com.woowacourse.zzimkkong.domain.ServiceZone; | ||
import com.woowacourse.zzimkkong.domain.Setting; | ||
import com.woowacourse.zzimkkong.domain.Settings; | ||
import com.woowacourse.zzimkkong.domain.Space; | ||
import com.woowacourse.zzimkkong.domain.TimeSlot; | ||
import com.woowacourse.zzimkkong.dto.member.LoginUserEmail; | ||
import com.woowacourse.zzimkkong.dto.reservation.*; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationAuthenticationDto; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationCreateDto; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationCreateResponse; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationFindAllDto; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationFindAllResponse; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationFindDto; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationFindResponse; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationInfiniteScrollResponse; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationResponse; | ||
import com.woowacourse.zzimkkong.dto.reservation.ReservationUpdateDto; | ||
import com.woowacourse.zzimkkong.dto.slack.SlackResponse; | ||
import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; | ||
import com.woowacourse.zzimkkong.exception.member.NoSuchMemberException; | ||
import com.woowacourse.zzimkkong.exception.reservation.*; | ||
import com.woowacourse.zzimkkong.exception.reservation.DeleteExpiredReservationException; | ||
import com.woowacourse.zzimkkong.exception.reservation.DeleteReservationInUseException; | ||
import com.woowacourse.zzimkkong.exception.reservation.InvalidMaximumDurationTimeException; | ||
import com.woowacourse.zzimkkong.exception.reservation.InvalidMinimumDurationTimeException; | ||
import com.woowacourse.zzimkkong.exception.reservation.InvalidMinimumDurationTimeInEarlyStopException; | ||
import com.woowacourse.zzimkkong.exception.reservation.InvalidReservationEnableException; | ||
import com.woowacourse.zzimkkong.exception.reservation.InvalidStartEndTimeException; | ||
import com.woowacourse.zzimkkong.exception.reservation.InvalidTimeUnitException; | ||
import com.woowacourse.zzimkkong.exception.reservation.NoSuchReservationException; | ||
import com.woowacourse.zzimkkong.exception.reservation.NotCurrentReservationException; | ||
import com.woowacourse.zzimkkong.exception.reservation.ReservationAlreadyExistsException; | ||
import com.woowacourse.zzimkkong.exception.setting.MultipleSettingsException; | ||
import com.woowacourse.zzimkkong.exception.setting.NoSettingAvailableException; | ||
import com.woowacourse.zzimkkong.exception.space.NoSuchSpaceException; | ||
|
@@ -15,7 +42,11 @@ | |
import com.woowacourse.zzimkkong.repository.MapRepository; | ||
import com.woowacourse.zzimkkong.repository.MemberRepository; | ||
import com.woowacourse.zzimkkong.repository.ReservationRepository; | ||
import com.woowacourse.zzimkkong.service.strategy.*; | ||
import com.woowacourse.zzimkkong.service.strategy.ExcludeReservationCreateStrategy; | ||
import com.woowacourse.zzimkkong.service.strategy.ExcludeReservationStrategy; | ||
import com.woowacourse.zzimkkong.service.strategy.ExcludeReservationUpdateStrategy; | ||
import com.woowacourse.zzimkkong.service.strategy.ReservationStrategies; | ||
import com.woowacourse.zzimkkong.service.strategy.ReservationStrategy; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
import org.springframework.stereotype.Service; | ||
|
@@ -24,6 +55,8 @@ | |
import java.time.DayOfWeek; | ||
import java.time.LocalDate; | ||
import java.time.LocalDateTime; | ||
import java.time.LocalTime; | ||
import java.time.temporal.ChronoUnit; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Comparator; | ||
|
@@ -219,6 +252,52 @@ public SlackResponse updateReservation(final ReservationUpdateDto reservationUpd | |
return SlackResponse.of(reservation, map); | ||
} | ||
|
||
public SlackResponse updateReservationEndTime(final ReservationUpdateDto reservationUpdateDto) { | ||
ReservationStrategy reservationStrategy = reservationStrategies.getStrategyByReservationType(reservationUpdateDto.getReservationType()); | ||
Long mapId = reservationUpdateDto.getMapId(); | ||
LoginUserEmail loginUserEmail = reservationUpdateDto.getLoginUserEmail(); | ||
|
||
Map map = maps.findByIdFetch(mapId) | ||
.orElseThrow(NoSuchMapException::new); | ||
reservationStrategy.validateManagerOfMap(map, loginUserEmail); | ||
|
||
Long reservationId = reservationUpdateDto.getReservationId(); | ||
Reservation reservation = reservations.findById(reservationId) | ||
.orElseThrow(NoSuchReservationException::new); | ||
reservationStrategy.validateOwnerOfReservation(reservation, reservationUpdateDto.getPassword(), loginUserEmail); | ||
|
||
LocalDateTime now = LocalDateTime.now(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 만드신 DTO There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. request에 선언되어 있던 now를 제거하고, service에서 한 번만 선언하여 사용하도록 수정하겠습니다! |
||
if (!reservation.isInUse(now)) { | ||
throw new NotCurrentReservationException(); | ||
} | ||
|
||
if (ChronoUnit.MINUTES.between(reservation.getStartTime(), reservationUpdateDto.getEndDateTime()) < 5L) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 5분이 아니라 예약시간단위( +) 사용자는 어떻게든 조기종료만 되면 되니까 최소 이용시간을 체크해서 예외를 날리는 것보다는 예약시간단위로 나누어떨어지는 가장 가까운 시간으로 종료시간이 설정되주는 게 사용성은 가장 좋을 것 같다는 생각도 드네요.
으로 종료시간을 업데이트 하도록 하고 제가 놓치고 있는 게 있다면 알려주세욥 '-' @sakjung There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 저도 회의때
근데 이 부분에서 좀 우려되는 게 있네요. 만약 reservationTimeUnit이 60 분이고 조기종료를 사용 후 5분 만에 했으면 60분 후 종료로 기록이 됩니다. 그러면 55분이나 붕뜨게 되고 조기 종료하는 의미가 퇴색될 것 같습니다. 그래서 그 경우에는 그냥 exception 내던가 아얘 삭제하던가 하는 식이 맞을 것 같아요. 아니면 프론트 측에서 reservationTimeUnit 시간 검증을 같이 해준다면 바다가 제안주신 방법과 비슷하게 가능할 것 같습니다.
아래는 reservationTimeUnit 를 가져올 수 있는 pseudo code 입니다. 객체 지향적으로 짠다면 해당 로직을 reservation 객체 내부에 메서드로 만드는게 좋겠네요! getMatchingSpaceSetting() 같은? (참고로 하나의 space에 다수의 setting이 귀속될 수 있습니다)
또 추가로 필요한 것은 그 때 회의때 얘기했듯이, 공간 최소 예약 시간 조건 ( 대략 예상 되는 작업은 다음과 같습니다.
|
||
throw new InvalidMinimumDurationTimeInEarlyStopException(); | ||
} | ||
|
||
reservation.updateReservationTime( | ||
ReservationTime.of( | ||
reservation.getStartTime(), | ||
LocalDateTime.of( | ||
reservation.getDate(), | ||
LocalTime.of( | ||
reservation.getEndTime().getHour(), | ||
floorByFiveMinutes(reservationUpdateDto.getEndDateTime()) | ||
) | ||
), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
reservation.getDate() 는 단순 indexing (조회) 용이고 service zone 기준 (KST 기준) 입니다. reservation.getEndTime() 은 UTC 시간입니다 (서버내의 모든 시간은 UTC로 관리). 이런 경우에는 다음과 같이 하면 될 것 같습니다 (아래 멘션드린 floor 관련 코드를 사용했습니다)
쭉 리뷰를 달다보니까,
최대한 다음과 같은 느낌으로 작성을 해보시는 건 어떨까요? 내부 구현은 저희가 단 리뷰들 참고하셔서 멋지게 작성해주세요 ㅎㅎ
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제안해주신 코드를 바탕으로 리팩토링 진행해보겠습니다! |
||
map.getServiceZone(), | ||
false) | ||
); | ||
|
||
map.activateSharingMapId(sharingIdGenerator); | ||
|
||
return SlackResponse.of(reservation, map); | ||
} | ||
|
||
private int floorByFiveMinutes(final LocalDateTime baseTime) { | ||
return (baseTime.getMinute() - 5) / 5 * 5; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도 5로 하드코딩 하시면 안되고 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
public SlackResponse deleteReservation(final ReservationAuthenticationDto reservationAuthenticationDto) { | ||
ReservationStrategy reservationStrategy = reservationStrategies.getStrategyByReservationType(reservationAuthenticationDto.getReservationType()); | ||
Long mapId = reservationAuthenticationDto.getMapId(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -149,4 +149,4 @@ private static Stream<Arguments> provideTimeSlotArguments_isNotWithin() { | |
false) | ||
); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
본인의 예약을 종료시키는 상황이니까 슬랙으로 예약수정알림은 따로 안보내줘도 될 것 같아요
(수정보다는 조기종료에 대한 알림케이스를 새로 만들어서 발송해줘도 되구용)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
조기 종료에 대한 알림 케이스를 새로 만드는 방식으로 구현해보겠습니다!